Exemplo n.º 1
0
        public virtual async Task <APIGatewayProxyResponse> FunctionHandlerAsync(APIGatewayProxyRequest request, ILambdaContext lambdaContext)
        {
            lambdaContext.Logger.LogLine($"Incoming {request.HttpMethod} requests to {request.Path}");

            InvokeFeatures features = new InvokeFeatures();

            MarshallRequest(features, request, lambdaContext);
            lambdaContext.Logger.LogLine($"ASP.NET Core Request PathBase: {((IHttpRequestFeature)features).PathBase}, Path: {((IHttpRequestFeature)features).Path}");

            var context = this.CreateContext(features);

            if (request?.RequestContext?.Authorizer?.Claims != null)
            {
                var identity = new ClaimsIdentity(request.RequestContext.Authorizer.Claims.Select(
                                                      entry => new Claim(entry.Key, entry.Value.ToString())), "AuthorizerIdentity");

                lambdaContext.Logger.LogLine($"Configuring HttpContext.User with {request.RequestContext.Authorizer.Claims.Count} claims coming from API Gateway's Request Context");
                context.HttpContext.User = new ClaimsPrincipal(identity);
            }

            // Add along the Lambda objects to the HttpContext to give access to Lambda to them in the ASP.NET Core application
            context.HttpContext.Items[LAMBDA_CONTEXT]     = lambdaContext;
            context.HttpContext.Items[APIGATEWAY_REQUEST] = request;

            // Allow the context to be customized before passing the request to ASP.NET Core.
            PostCreateContext(context, request, lambdaContext);

            var response = await this.ProcessRequest(lambdaContext, context, features);

            return(response);
        }
        public async Task <APIGatewayProxyResponse> FunctionHandlerAsync(APIGatewayAuthorizerProxyRequest request, ILambdaContext lambdaContext)
        {
            Console.WriteLine(JsonConvert.SerializeObject(request));
            lambdaContext.Logger.LogLine($"Incoming {request.HttpMethod} requests to {request.Path}");

            var features = new InvokeFeatures();

            MarshallRequest(features, request);

            var context = CreateContext(features);

            if (request.Headers.ContainsKey("Authorization"))
            {
                var token    = new JwtSecurityToken(request.Headers["Authorization"]);
                var identity = new ClaimsIdentity(token.Claims, "AuthorizerIdentity");
                context.HttpContext.User = new ClaimsPrincipal(identity);
            }

            // Add along the Lambda objects to the HttpContext to give access to Lambda to them in the ASP.NET Core application
            context.HttpContext.Items["LAMBDA_CONTEXT"]     = lambdaContext;
            context.HttpContext.Items["APIGATEWAY_REQUEST"] = request;

            var response = await ProcessRequest(lambdaContext, context, features);

            return(response);
        }
Exemplo n.º 3
0
        public virtual async Task <APIGatewayProxyResponse> FunctionHandlerAsync(APIGatewayProxyRequest request, ILambdaContext lambdaContext)
        {
            lambdaContext?.Logger.Log($"Incoming {request.HttpMethod} requests to {request.Path}");

            InvokeFeatures features = new InvokeFeatures();

            MarshallRequest(features, request);

            var context = _server.Application.CreateContext(features);

            try
            {
                await this._server.Application.ProcessRequestAsync(context);

                this._server.Application.DisposeContext(context, null);
            }
            catch (Exception e)
            {
                lambdaContext?.Logger.Log($"Unknown error responding to request: {ErrorReport(e)}");
                this._server.Application.DisposeContext(context, e);
            }

            var response = MarshallResponse(features);

            // ASP.NET Core Web API does not always set the status code if the request was
            // successful
            if (response.StatusCode == 0)
            {
                response.StatusCode = 200;
            }

            return(response);
        }
Exemplo n.º 4
0
        /// <inheritdoc/>
        protected override void MarshallRequest(InvokeFeatures features, ApplicationLoadBalancerRequest lambdaRequest, ILambdaContext lambdaContext)
        {
            // Request coming from Application Load Balancer will always send the headers X-Amzn-Trace-Id, X-Forwarded-For, X-Forwarded-Port, and X-Forwarded-Proto.
            // So this will only happen when writing tests with incomplete sample requests.
            if (lambdaRequest.Headers == null && lambdaRequest.MultiValueHeaders == null)
            {
                throw new Exception("Unable to determine header mode, single or multi value, because both Headers and MultiValueHeaders are null.");
            }

            // Look to see if the request is using mutli value headers or not. This is important when
            // marshalling the response to know whether to fill in the the Headers or MultiValueHeaders collection.
            // Since a Lambda function compute environment is only one processing one event at a time it is safe to store
            // this as a member variable.
            this._multiHeaderValuesEnabled = lambdaRequest.MultiValueHeaders != null;

            {
                var requestFeatures = (IHttpRequestFeature)features;
                requestFeatures.Scheme = GetSingleHeaderValue(lambdaRequest, "x-forwarded-proto");
                requestFeatures.Method = lambdaRequest.HttpMethod;
                requestFeatures.Path   = Utilities.DecodeResourcePath(lambdaRequest.Path);

                requestFeatures.QueryString = Utilities.CreateQueryStringParamaters(
                    lambdaRequest.QueryStringParameters, lambdaRequest.MultiValueQueryStringParameters);

                Utilities.SetHeadersCollection(requestFeatures.Headers, lambdaRequest.Headers, lambdaRequest.MultiValueHeaders);

                if (!string.IsNullOrEmpty(lambdaRequest.Body))
                {
                    requestFeatures.Body = Utilities.ConvertLambdaRequestBodyToAspNetCoreBody(lambdaRequest.Body, lambdaRequest.IsBase64Encoded);
                }

                // Call consumers customize method in case they want to change how API Gateway's request
                // was marshalled into ASP.NET Core request.
                PostMarshallRequestFeature(requestFeatures, lambdaRequest, lambdaContext);
            }


            {
                // set up connection features
                var connectionFeatures = (IHttpConnectionFeature)features;

                var remoteIpAddressStr = GetSingleHeaderValue(lambdaRequest, "x-forwarded-for");
                if (!string.IsNullOrEmpty(remoteIpAddressStr) &&
                    IPAddress.TryParse(remoteIpAddressStr, out var remoteIpAddress))
                {
                    connectionFeatures.RemoteIpAddress = remoteIpAddress;
                }

                var remotePort = GetSingleHeaderValue(lambdaRequest, "x-forwarded-port");
                if (!string.IsNullOrEmpty(remotePort))
                {
                    connectionFeatures.RemotePort = int.Parse(remotePort);
                }

                // Call consumers customize method in case they want to change how API Gateway's request
                // was marshalled into ASP.NET Core request.
                PostMarshallConnectionFeature(connectionFeatures, lambdaRequest, lambdaContext);
            }
        }
Exemplo n.º 5
0
        public void EnsureTraceIdStaysTheSame()
        {
            var features = new InvokeFeatures() as IHttpRequestIdentifierFeature;

            var traceId1 = features.TraceIdentifier;
            var traceId2 = features.TraceIdentifier;

            Assert.Equal(traceId1, traceId2);
        }
        public virtual async Task <APIGatewayProxyResponse> FunctionHandlerAsync(APIGatewayProxyRequest request, ILambdaContext lambdaContext)
        {
            lambdaContext.Logger.Log($"Incoming {request.HttpMethod} requests to {request.Path}");
            InvokeFeatures features = new InvokeFeatures();

            MarshallRequest(features, request);
            var context = this.CreateContext(features);

            return(await this.ProcessRequest(lambdaContext, context, features));
        }
        public async Task <APIGatewayProxyResponse> FunctionHandlerAsync(APIGatewayAuthorizerProxyRequest request, ILambdaContext lambdaContext)
        {
            Console.WriteLine(JsonConvert.SerializeObject(request));
            if (string.IsNullOrEmpty(request.HttpMethod))
            {
                request.HttpMethod = "GET";
                request.Path       = "/ping";
                request.Headers    = new Dictionary <string, string> {
                    { "Host", "localhost" }
                };
                request.RequestContext = new AuthorizerRequestContext();

                lambdaContext.Logger.LogLine("Keep-alive invokation");
                lambdaContext = new DummyContext(lambdaContext);
            }
            lambdaContext.Logger.LogLine($"Incoming {request.HttpMethod} requests to {request.Path}");
            var features = new InvokeFeatures();

            MarshallRequest(features, request);

            var context = this.CreateContext(features);

            if (request.RequestContext.Authorizer != null)
            {
                var claims   = request.RequestContext.Authorizer.Claims.ToObject <Dictionary <string, object> >();
                var identity = new ClaimsIdentity(claims.Select(entry => new Claim(entry.Key, entry.Value.ToString())), "AuthorizerIdentity");
                context.HttpContext.User = new ClaimsPrincipal(identity);
            }

            // Add along the Lambda objects to the HttpContext to give access to Lambda to them in the ASP.NET Core application
            context.HttpContext.Items[LAMBDA_CONTEXT]     = lambdaContext;
            context.HttpContext.Items[APIGATEWAY_REQUEST] = request;

            var response = await this.ProcessRequest(lambdaContext, context, features);

            var useGzip = false;

            if (request.Headers != null && request.Headers.TryGetValue("accept-encoding", out var headerValue))
            {
                useGzip = headerValue.Contains("gzip");
            }

            if (useGzip && context.HttpContext.Request.Method != HttpMethod.Options.Method)
            {
                response.IsBase64Encoded = true;
                var buffer = Zip(response.Body);
                response.Body = Convert.ToBase64String(buffer);
                response.Headers.Add(HeaderNames.ContentEncoding, "gzip");
            }

            return(response);
        }
Exemplo n.º 8
0
        protected override void MarshallRequest(InvokeFeatures features, APIGatewayProxyRequest apiGatewayRequest, ILambdaContext lambdaContext)
        {
            //string serializedRequest = JsonConvert.SerializeObject(apiGatewayRequest);
            lambdaContext.Logger.LogLine($"REQUEST: {JsonConvert.SerializeObject(apiGatewayRequest)}");
            lambdaContext.Logger.LogLine($"REQUEST Context: {JsonConvert.SerializeObject(apiGatewayRequest.RequestContext)}");

            if (apiGatewayRequest.RequestContext == null)
            {
                CallHealthCheck(lambdaContext);
            }
            else
            {
                base.MarshallRequest(features, apiGatewayRequest, lambdaContext);
            }
        }
        public virtual async Task <APIGatewayProxyResponse> FunctionHandlerAsync(APIGatewayProxyRequest request, ILambdaContext lambdaContext)
        {
            lambdaContext.Logger.LogLine($"Incoming {request.HttpMethod} requests to {request.Path}");
            InvokeFeatures features = new InvokeFeatures();

            MarshallRequest(features, request);
            var context = this.CreateContext(features);

            // Add along the Lambda objects to the HttpContext to give access to Lambda to them in the ASP.NET Core application
            context.HttpContext.Items[LAMBDA_CONTEXT]     = lambdaContext;
            context.HttpContext.Items[APIGATEWAY_REQUEST] = request;

            var response = await this.ProcessRequest(lambdaContext, context, features);

            return(response);
        }
        /// <summary>
        /// This method is what the Lambda function handler points to.
        /// </summary>
        /// <param name="requestStream">Takes in a Stream instead of APIGatewayProxyRequest to get access to the raw event for logging purposes.</param>
        /// <param name="lambdaContext"></param>
        /// <returns></returns>
        public virtual async Task <Stream> FunctionHandlerAsync(Stream requestStream, ILambdaContext lambdaContext)
        {
            if (this.EnableRequestLogging)
            {
                StreamReader reader = new StreamReader(requestStream);
                string       json   = reader.ReadToEnd();
                lambdaContext.Logger.LogLine(json);
                requestStream.Position = 0;
            }

            var request = this._serializer.Deserialize <APIGatewayProxyRequest>(requestStream);

            lambdaContext.Logger.Log($"Incoming {request.HttpMethod} requests to {request.Path}");
            InvokeFeatures features = new InvokeFeatures();

            MarshallRequest(features, request);
            var context = this.CreateContext(features);

            var response = await this.ProcessRequest(lambdaContext, context, features);

            var responseStream = new MemoryStream();

            this._serializer.Serialize <APIGatewayProxyResponse>(response, responseStream);
            responseStream.Position = 0;

            if (this.EnableResponseLogging)
            {
                StreamReader reader = new StreamReader(responseStream);
                string       json   = reader.ReadToEnd();
                lambdaContext.Logger.LogLine(json);
                responseStream.Position = 0;
            }


            return(responseStream);
        }
        /// <summary>
        /// Convert the JSON document received from API Gateway into the InvokeFeatures object.
        /// InvokeFeatures is then passed into IHttpApplication to create the ASP.NET Core request objects.
        /// </summary>
        /// <param name="features"></param>
        /// <param name="apiGatewayRequest"></param>
        /// <param name="lambdaContext"></param>
        protected override void MarshallRequest(InvokeFeatures features, APIGatewayHttpApiV2ProxyRequest 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.Jwt?.Claims != null && authorizer.Jwt.Claims.Count != 0)
                    {
                        var identity = new ClaimsIdentity(authorizer.Jwt.Claims.Select(
                                                              entry => new Claim(entry.Key, entry.Value.ToString())), "AuthorizerIdentity");

                        _logger.LogDebug(
                            $"Configuring HttpContext.User with {authorizer.Jwt.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.Jwt?.Claims.Select(entry => new Claim(entry.Key, entry.Value)), "AuthorizerIdentity");

                        _logger.LogDebug(
                            $"Configuring HttpContext.User with {identity.Claims.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 httpInfo        = apiGatewayRequest.RequestContext.Http;
                var requestFeatures = (IHttpRequestFeature)features;
                requestFeatures.Scheme = "https";
                requestFeatures.Method = httpInfo.Method;

                if (string.IsNullOrWhiteSpace(apiGatewayRequest.RequestContext?.DomainName))
                {
                    _logger.LogWarning($"Request does not contain domain name information but is derived from {nameof(APIGatewayProxyFunction)}.");
                }

                requestFeatures.Path = Utilities.DecodeResourcePath(httpInfo.Path);
                if (!requestFeatures.Path.StartsWith("/"))
                {
                    requestFeatures.Path = "/" + requestFeatures.Path;
                }


                // If there is a stage name in the resource path strip it out and set the stage name as the base path.
                // This is required so that ASP.NET Core will route request based on the resource path without the stage name.
                var stageName = apiGatewayRequest.RequestContext.Stage;
                if (!string.IsNullOrWhiteSpace(stageName))
                {
                    if (requestFeatures.Path.StartsWith($"/{stageName}"))
                    {
                        requestFeatures.Path     = requestFeatures.Path.Substring(stageName.Length + 1);
                        requestFeatures.PathBase = $"/{stageName}";
                    }
                }

                requestFeatures.QueryString = Utilities.CreateQueryStringParametersFromHttpApiV2(apiGatewayRequest.RawQueryString);

                // API Gateway HTTP API V2 format supports multiple values for headers by comma separating the values.
                if (apiGatewayRequest.Headers != null)
                {
                    foreach (var kvp in apiGatewayRequest.Headers)
                    {
                        requestFeatures.Headers[kvp.Key] = new StringValues(kvp.Value?.Split(','));
                    }
                }

                if (!requestFeatures.Headers.ContainsKey("Host"))
                {
                    requestFeatures.Headers["Host"] = apiGatewayRequest.RequestContext.DomainName;
                }

                if (apiGatewayRequest.Cookies != null)
                {
                    // Add Cookies from the event
                    requestFeatures.Headers["Cookie"] = String.Join("; ", apiGatewayRequest.Cookies);
                }

                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?.Http?.SourceIp) &&
                    IPAddress.TryParse(apiGatewayRequest.RequestContext.Http.SourceIp, out var remoteIpAddress))
                {
                    connectionFeatures.RemoteIpAddress = remoteIpAddress;
                }

                if (apiGatewayRequest?.Headers?.TryGetValue("X-Forwarded-Port", out var port) == true)
                {
                    connectionFeatures.RemotePort = int.Parse(port, 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?.Authentication?.ClientCert?.ClientCertPem;
                if (clientCertPem != null)
                {
                    tlsConnectionFeature.ClientCertificate = Utilities.GetX509Certificate2FromPem(clientCertPem);
                }

                PostMarshallTlsConnectionFeature(tlsConnectionFeature, apiGatewayRequest, lambdaContext);
            }
        }
        /// <summary>
        /// Convert the JSON document received from API Gateway into the InvokeFeatures object.
        /// InvokeFeatures is then passed into IHttpApplication to create the ASP.NET Core request objects.
        /// </summary>
        /// <param name="features"></param>
        /// <param name="apiGatewayRequest"></param>
        /// <param name="lambdaContext"></param>
        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;

                if (string.IsNullOrWhiteSpace(apiGatewayRequest?.RequestContext?.DomainName))
                {
                    _logger.LogWarning($"Request does not contain domain name information but is derived from {nameof(APIGatewayProxyFunction)}.");
                }

                string path = null;
                if (apiGatewayRequest.PathParameters != null && apiGatewayRequest.PathParameters.ContainsKey("proxy") &&
                    !string.IsNullOrEmpty(apiGatewayRequest.Resource))
                {
                    var proxyPath = apiGatewayRequest.PathParameters["proxy"];
                    path = apiGatewayRequest.Resource.Replace("{proxy+}", proxyPath);
                }

                if (string.IsNullOrEmpty(path))
                {
                    path = apiGatewayRequest.Path;
                }

                if (!path.StartsWith("/"))
                {
                    path = "/" + path;
                }

                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);

                requestFeatures.QueryString = Utilities.CreateQueryStringParameters(
                    apiGatewayRequest.QueryStringParameters, apiGatewayRequest.MultiValueQueryStringParameters, true);

                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?.ContainsKey("X-Forwarded-Port") == true)
                {
                    connectionFeatures.RemotePort = int.Parse(apiGatewayRequest.Headers["X-Forwarded-Port"]);
                }

                // 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);
            }
        }
Exemplo n.º 13
0
        /// <summary>
        /// Convert the JSON document received from API Gateway into the InvokeFeatures object.
        /// InvokeFeatures is then passed into IHttpApplication to create the ASP.NET Core request objects.
        /// </summary>
        /// <param name="features"></param>
        /// <param name="apiGatewayRequest"></param>
        /// <param name="lambdaContext"></param>
        protected void MarshallRequest(InvokeFeatures features, APIGatewayProxyRequest apiGatewayRequest, ILambdaContext lambdaContext)
        {
            {
                var requestFeatures = (IHttpRequestFeature)features;
                requestFeatures.Scheme = "https";
                requestFeatures.Method = apiGatewayRequest.HttpMethod;

                string path = null;
                if (apiGatewayRequest.PathParameters != null && apiGatewayRequest.PathParameters.ContainsKey("proxy") && !string.IsNullOrEmpty(apiGatewayRequest.Resource))
                {
                    var proxyPath = apiGatewayRequest.PathParameters["proxy"];
                    path = apiGatewayRequest.Resource.Replace("{proxy+}", proxyPath);
                }

                if (string.IsNullOrEmpty(path))
                {
                    path = apiGatewayRequest.Path;
                }

                if (!path.StartsWith("/"))
                {
                    path = "/" + path;
                }

                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 += "/";
                    }

                    if (requestContextPath.EndsWith(path))
                    {
                        requestFeatures.PathBase = requestContextPath.Substring(0, requestContextPath.Length - requestFeatures.Path.Length);
                    }
                }


                // API Gateway delivers the query string in a dictionary but must be reconstructed into the full query string
                // before passing into ASP.NET Core framework.
                var queryStringParameters = apiGatewayRequest.QueryStringParameters;
                if (queryStringParameters != null)
                {
                    StringBuilder sb = new StringBuilder("?");
                    foreach (var kvp in queryStringParameters)
                    {
                        if (sb.Length > 1)
                        {
                            sb.Append("&");
                        }
                        sb.Append($"{WebUtility.UrlEncode(kvp.Key)}={WebUtility.UrlEncode(kvp.Value.ToString())}");
                    }
                    requestFeatures.QueryString = sb.ToString();
                }
                else
                {
                    requestFeatures.QueryString = string.Empty;
                }

                var headers = apiGatewayRequest.Headers;
                if (headers != null)
                {
                    foreach (var kvp in headers)
                    {
                        requestFeatures.Headers[kvp.Key] = kvp.Value?.ToString();
                    }
                }

                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))
                {
                    Byte[] binaryBody;
                    if (apiGatewayRequest.IsBase64Encoded)
                    {
                        binaryBody = Convert.FromBase64String(apiGatewayRequest.Body);
                    }
                    else
                    {
                        binaryBody = UTF8Encoding.UTF8.GetBytes(apiGatewayRequest.Body);
                    }
                    requestFeatures.Body = new MemoryStream(binaryBody);
                }

                // 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;

                IPAddress remoteIpAddress;
                if (!string.IsNullOrEmpty(apiGatewayRequest?.RequestContext?.Identity?.SourceIp) &&
                    IPAddress.TryParse(apiGatewayRequest.RequestContext.Identity.SourceIp, out remoteIpAddress))
                {
                    connectionFeatures.RemoteIpAddress = remoteIpAddress;
                }

                if (apiGatewayRequest?.Headers?.ContainsKey("X-Forwarded-Port") == true)
                {
                    connectionFeatures.RemotePort = int.Parse(apiGatewayRequest.Headers["X-Forwarded-Port"]);
                }

                // 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);
            }
        }
Exemplo n.º 14
0
        /// <summary>
        /// Processes the current request.
        /// </summary>
        /// <param name="lambdaContext"><see cref="ILambdaContext"/> implementation.</param>
        /// <param name="context">The hosting application request context object.</param>
        /// <param name="features">An <see cref="InvokeFeatures"/> instance.</param>
        /// <param name="rethrowUnhandledError">
        /// If specified, an unhandled exception will be rethrown for custom error handling.
        /// Ensure that the error handling code calls 'this.MarshallResponse(features, 500);' after handling the error to return a <see cref="APIGatewayProxyResponse"/> to the user.
        /// </param>
        protected async Task <APIGatewayProxyResponse> ProcessRequest(ILambdaContext lambdaContext, HostingApplication.Context context, InvokeFeatures features, bool rethrowUnhandledError = false)
        {
            var       defaultStatusCode = 200;
            Exception ex = null;

            try
            {
                await this._server.Application.ProcessRequestAsync(context);
            }
            catch (AggregateException agex)
            {
                ex = agex;
                lambdaContext.Logger.LogLine($"Caught AggregateException: '{agex}'");
                var sb = new StringBuilder();
                foreach (var newEx in agex.InnerExceptions)
                {
                    sb.AppendLine(this.ErrorReport(newEx));
                }

                lambdaContext.Logger.LogLine(sb.ToString());
                defaultStatusCode = 500;
            }
            catch (ReflectionTypeLoadException rex)
            {
                ex = rex;
                lambdaContext.Logger.LogLine($"Caught ReflectionTypeLoadException: '{rex}'");
                var sb = new StringBuilder();
                foreach (var loaderException in rex.LoaderExceptions)
                {
                    var fileNotFoundException = loaderException as FileNotFoundException;
                    if (fileNotFoundException != null && !string.IsNullOrEmpty(fileNotFoundException.FileName))
                    {
                        sb.AppendLine($"Missing file: {fileNotFoundException.FileName}");
                    }
                    else
                    {
                        sb.AppendLine(this.ErrorReport(loaderException));
                    }
                }

                lambdaContext.Logger.LogLine(sb.ToString());
                defaultStatusCode = 500;
            }
            catch (Exception e)
            {
                ex = e;
                if (rethrowUnhandledError)
                {
                    throw;
                }
                lambdaContext.Logger.LogLine($"Unknown error responding to request: {this.ErrorReport(e)}");
                defaultStatusCode = 500;
            }
            finally
            {
                this._server.Application.DisposeContext(context, ex);
            }

            if (features.ResponseStartingEvents != null)
            {
                await features.ResponseStartingEvents.ExecuteAsync();
            }
            var response = this.MarshallResponse(features, lambdaContext, defaultStatusCode);

            lambdaContext.Logger.LogLine($"Response Base 64 Encoded: {response.IsBase64Encoded}");

            if (ex != null)
            {
                response.Headers.Add(new KeyValuePair <string, string>("ErrorType", ex.GetType().Name));
            }

            if (features.ResponseCompletedEvents != null)
            {
                await features.ResponseCompletedEvents.ExecuteAsync();
            }

            return(response);
        }
Exemplo n.º 15
0
        protected async Task <APIGatewayProxyResponse> MyProcessRequest(ILambdaContext lambdaContext,
                                                                        HostingApplication.Context context, InvokeFeatures features,
                                                                        bool rethrowUnhandledError = false)
        {
            _logger.LogInformation("MY PROCESS REQUEST!!!");

            var resp = await base.ProcessRequest(lambdaContext, context, features, rethrowUnhandledError);

            if (resp.Body != null && resp.Headers.ContainsKey("Content-Type"))
            {
                if ("application/octet-stream" == resp.Headers["Content-Type"])
                {
                    _logger.LogInformation("SETTING B64!!!");

                    resp = new MyAPIGatewayProxyResponse
                    {
                        Body            = resp.Body,
                        Headers         = resp.Headers,
                        StatusCode      = resp.StatusCode,
                        IsBase64Encoded = true,
                    };
                }
            }

            return(resp);
        }
Exemplo n.º 16
0
        /// <summary>
        /// Convert the JSON document received from API Gateway into the InvokeFeatures object.
        /// InvokeFeatures is then passed into IHttpApplication to create the ASP.NET Core request objects.
        /// </summary>
        /// <param name="features"></param>
        /// <param name="apiGatewayRequest"></param>
        /// <param name="lambdaContext"></param>
        protected override void MarshallRequest(InvokeFeatures features, APIGatewayProxyRequest apiGatewayRequest, ILambdaContext lambdaContext)
        {
            {
                var requestFeatures = (IHttpRequestFeature)features;
                requestFeatures.Scheme = "https";
                requestFeatures.Method = apiGatewayRequest.HttpMethod;

                string path = null;
                if (apiGatewayRequest.PathParameters != null && apiGatewayRequest.PathParameters.ContainsKey("proxy") &&
                    !string.IsNullOrEmpty(apiGatewayRequest.Resource))
                {
                    var proxyPath = apiGatewayRequest.PathParameters["proxy"];
                    path = apiGatewayRequest.Resource.Replace("{proxy+}", proxyPath);
                }

                if (string.IsNullOrEmpty(path))
                {
                    path = apiGatewayRequest.Path;
                }

                if (!path.StartsWith("/"))
                {
                    path = "/" + path;
                }

                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);

                requestFeatures.QueryString = Utilities.CreateQueryStringParameters(
                    apiGatewayRequest.QueryStringParameters, apiGatewayRequest.MultiValueQueryStringParameters, true);

                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);
                }

                // 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?.ContainsKey("X-Forwarded-Port") == true)
                {
                    connectionFeatures.RemotePort = int.Parse(apiGatewayRequest.Headers["X-Forwarded-Port"]);
                }

                // 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);
            }
        }
        /// <inheritdoc/>
        protected override void MarshallRequest(InvokeFeatures features, ApplicationLoadBalancerRequest lambdaRequest, ILambdaContext lambdaContext)
        {
            // Call consumers customize method in case they want to change how API Gateway's request
            // was marshalled into ASP.NET Core request.
            PostMarshallHttpAuthenticationFeature(features, lambdaRequest, lambdaContext);

            // Request coming from Application Load Balancer will always send the headers X-Amzn-Trace-Id, X-Forwarded-For, X-Forwarded-Port, and X-Forwarded-Proto.
            // So this will only happen when writing tests with incomplete sample requests.
            if (lambdaRequest.Headers == null && lambdaRequest.MultiValueHeaders == null)
            {
                throw new Exception("Unable to determine header mode, single or multi value, because both Headers and MultiValueHeaders are null.");
            }

            if (lambdaRequest.RequestContext?.Elb?.TargetGroupArn == null)
            {
                _logger.LogWarning($"Request does not contain ELB information but is derived from {nameof(ApplicationLoadBalancerFunction)}.");
            }

            // Look to see if the request is using mutli value headers or not. This is important when
            // marshalling the response to know whether to fill in the the Headers or MultiValueHeaders collection.
            // Since a Lambda function compute environment is only one processing one event at a time it is safe to store
            // this as a member variable.
            this._multiHeaderValuesEnabled = lambdaRequest.MultiValueHeaders != null;

            {
                var requestFeatures = (IHttpRequestFeature)features;
                requestFeatures.Scheme = GetSingleHeaderValue(lambdaRequest, "x-forwarded-proto");
                requestFeatures.Method = lambdaRequest.HttpMethod;
                requestFeatures.Path   = Utilities.DecodeResourcePath(lambdaRequest.Path);

                requestFeatures.QueryString = Utilities.CreateQueryStringParameters(
                    lambdaRequest.QueryStringParameters, lambdaRequest.MultiValueQueryStringParameters, false);

                Utilities.SetHeadersCollection(requestFeatures.Headers, lambdaRequest.Headers, lambdaRequest.MultiValueHeaders);

                if (!string.IsNullOrEmpty(lambdaRequest.Body))
                {
                    requestFeatures.Body = Utilities.ConvertLambdaRequestBodyToAspNetCoreBody(lambdaRequest.Body, lambdaRequest.IsBase64Encoded);
                }

                var userAgent = GetSingleHeaderValue(lambdaRequest, "user-agent");
                if (userAgent != null && userAgent.StartsWith("ELB-HealthChecker/", StringComparison.OrdinalIgnoreCase))
                {
                    requestFeatures.Scheme                      = "https";
                    requestFeatures.Headers["host"]             = "localhost";
                    requestFeatures.Headers["x-forwarded-port"] = "443";
                    requestFeatures.Headers["x-forwarded-for"]  = "127.0.0.1";
                }

                // Call consumers customize method in case they want to change how API Gateway's request
                // was marshalled into ASP.NET Core request.
                PostMarshallRequestFeature(requestFeatures, lambdaRequest, lambdaContext);
            }


            {
                // set up connection features
                var connectionFeatures = (IHttpConnectionFeature)features;

                var remoteIpAddressStr = GetSingleHeaderValue(lambdaRequest, "x-forwarded-for");
                if (!string.IsNullOrEmpty(remoteIpAddressStr) &&
                    IPAddress.TryParse(remoteIpAddressStr, out var remoteIpAddress))
                {
                    connectionFeatures.RemoteIpAddress = remoteIpAddress;
                }

                var remotePort = GetSingleHeaderValue(lambdaRequest, "x-forwarded-port");
                if (!string.IsNullOrEmpty(remotePort))
                {
                    connectionFeatures.RemotePort = int.Parse(remotePort);
                }

                // Call consumers customize method in case they want to change how API Gateway's request
                // was marshalled into ASP.NET Core request.
                PostMarshallConnectionFeature(connectionFeatures, lambdaRequest, lambdaContext);
            }
        }
        /// <summary>
        /// Convert the JSON document received from API Gateway into the InvokeFeatures object.
        /// InvokeFeatures is then passed into IHttpApplication to create the ASP.NET Core request objects.
        /// </summary>
        /// <param name="features"></param>
        /// <param name="apiGatewayRequest"></param>
        protected void MarshallRequest(InvokeFeatures features, APIGatewayProxyRequest apiGatewayRequest)
        {
            var requestFeatures = (IHttpRequestFeature)features;

            requestFeatures.Scheme = "https";
            requestFeatures.Method = apiGatewayRequest.HttpMethod;

            string path = null;

            if (apiGatewayRequest.PathParameters != null && apiGatewayRequest.PathParameters.ContainsKey("proxy"))
            {
                var proxyPath = apiGatewayRequest.PathParameters["proxy"];
                path = apiGatewayRequest.Resource.Replace("{proxy+}", proxyPath);
            }

            if (string.IsNullOrEmpty(path))
            {
                path = apiGatewayRequest.Path;
            }

            if (!path.StartsWith("/"))
            {
                path = "/" + path;
            }

            requestFeatures.Path = WebUtility.UrlDecode(path);

            // API Gateway delivers the query string in a dictionary but must be reconstructed into the full query string
            // before passing into ASP.NET Core framework.
            var queryStringParameters = apiGatewayRequest.QueryStringParameters;

            if (queryStringParameters != null)
            {
                StringBuilder sb = new StringBuilder("?");
                foreach (var kvp in queryStringParameters)
                {
                    if (sb.Length > 1)
                    {
                        sb.Append("&");
                    }
                    sb.Append($"{WebUtility.UrlEncode(kvp.Key)}={WebUtility.UrlEncode(kvp.Value.ToString())}");
                }
                requestFeatures.QueryString = sb.ToString();
            }

            var headers = apiGatewayRequest.Headers;

            if (headers != null)
            {
                foreach (var kvp in headers)
                {
                    requestFeatures.Headers[kvp.Key] = kvp.Value?.ToString();
                }
            }

            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))
            {
                Byte[] binaryBody;
                if (apiGatewayRequest.IsBase64Encoded)
                {
                    binaryBody = Convert.FromBase64String(apiGatewayRequest.Body);
                }
                else
                {
                    binaryBody = UTF8Encoding.UTF8.GetBytes(apiGatewayRequest.Body);
                }
                requestFeatures.Body = new MemoryStream(binaryBody);
            }

            // set up connection features
            var connectionFeatures = (IHttpConnectionFeature)features;

            IPAddress remoteIpAddress;

            if (!string.IsNullOrEmpty(apiGatewayRequest?.RequestContext?.Identity?.SourceIp) &&
                IPAddress.TryParse(apiGatewayRequest.RequestContext.Identity.SourceIp, out remoteIpAddress))
            {
                connectionFeatures.RemoteIpAddress = remoteIpAddress;
            }

            if (apiGatewayRequest?.Headers?.ContainsKey("X-Forwarded-Port") == true)
            {
                connectionFeatures.RemotePort = int.Parse(apiGatewayRequest.Headers["X-Forwarded-Port"]);
            }
        }
Exemplo n.º 19
0
        public void EnsureStatusCodeStartsAtIs200()
        {
            var feature = new InvokeFeatures() as IHttpResponseFeature;

            Assert.Equal(200, feature.StatusCode);
        }
        public virtual async Task <APIGatewayProxyResponse> FunctionHandlerAsync(Stream input, ILambdaContext lambdaContext)
        {
            if (!IsStarted)
            {
                Start();
            }

            try
            {
                string jsonInput = null;
                using (var reader = new StreamReader(input, Encoding.UTF8))
                {
                    jsonInput = reader.ReadToEnd();
                }

                if (jsonInput == null)
                {
                    throw new ArgumentNullException(nameof(input));
                }

                var zeitRequest = JsonConvert.DeserializeObject <ZeitRequest> (jsonInput);

                ZeitBody zeitBody = null;
                if (zeitRequest.Body != null)
                {
                    zeitBody = JsonConvert.DeserializeObject <ZeitBody> (zeitRequest.Body);
                }

                var request = new APIGatewayProxyRequest()
                {
                    Body            = zeitBody.Body,
                    Headers         = zeitBody.Headers,
                    Path            = zeitBody.Path,
                    HttpMethod      = zeitBody.Method,
                    IsBase64Encoded = zeitBody.Enconding?.Equals("base64") ?? false
                };

                _logger.LogDebug($"Incoming {request.HttpMethod} requests to {request.Path}");

                InvokeFeatures features = new InvokeFeatures();
                MarshallRequest(features, request, lambdaContext);
                _logger.LogDebug($"ASP.NET Core Request PathBase: {((IHttpRequestFeature)features).PathBase}, Path: {((IHttpRequestFeature)features).Path}");

                var context = this.CreateContext(features);

                if (request?.RequestContext?.Authorizer?.Claims != null)
                {
                    var identity = new ClaimsIdentity(request.RequestContext.Authorizer.Claims.Select(
                                                          entry => new Claim(entry.Key, entry.Value.ToString())), "AuthorizerIdentity");

                    _logger.LogDebug($"Configuring HttpContext.User with {request.RequestContext.Authorizer.Claims.Count} claims coming from API Gateway's Request Context");
                    context.HttpContext.User = new ClaimsPrincipal(identity);
                }

                // Add along the Lambda objects to the HttpContext to give access to Lambda to them in the ASP.NET Core application
                context.HttpContext.Items[LAMBDA_CONTEXT]     = lambdaContext;
                context.HttpContext.Items[APIGATEWAY_REQUEST] = request;

                // Allow the context to be customized before passing the request to ASP.NET Core.
                PostCreateContext(context, request, lambdaContext);

                var response = await this.ProcessRequest(lambdaContext, context, features);

                return(response);
            }
            catch (Exception e)
            {
                throw new Exception(e.Message + e.InnerException + e.StackTrace);
            }
        }