Skip to content

Commit

Permalink
feat(aws-lambda): added function url support
Browse files Browse the repository at this point in the history
ref 94000
#826
  • Loading branch information
aryamohanan authored and kirrg001 committed Jul 24, 2023
1 parent 827a722 commit 6f9fdb2
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 2 deletions.
35 changes: 34 additions & 1 deletion packages/aws-lambda/src/triggers.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const maxS3Records = 3;
const maxDynamoDbRecords = 3;
const maxS3ObjectKeyLength = 200;
const maxSQSRecords = 3;
const awsLambdaFunctionUrlHostRegex = /^.*\.lambda-url\..*\.on\.aws$/i;

exports.enrichSpanWithTriggerData = function enrichSpanWithTriggerData(event, context, span) {
if (isApiGatewayProxyTrigger(event)) {
Expand Down Expand Up @@ -52,6 +53,10 @@ exports.enrichSpanWithTriggerData = function enrichSpanWithTriggerData(event, co
} else if (isInvokeFunction(context)) {
span.data.lambda.trigger = 'aws:lambda.invoke';
return;
} else if (isFunctionURLTrigger(event)) {
span.data.lambda.trigger = 'aws:lambda.function.url';
extractFunctionUrlEvent(event, span);
return;
}

// When an API Gateway is used without the "Use Lambda Proxy" setting, the body from the HTTP request is forwarded
Expand All @@ -69,9 +74,25 @@ function isApiGatewayProxyTrigger(event) {
// NOTE: Gateway Rest API with lambda proxy -> always format 1.0
// NOTE: Gateway HTTP API with lambda proxy -> default is 2.0, but can be configured on creation
// NOTE: Gateway REST/HTTP API without lambda proxy -> format cannot be interpreted
// NOTE: Function URL also support version 2 and contains rawPath, Gateway HTTP API does not follow the format
// https://<url-id>.lambda-url.<region>.on.aws
return (
(event.resource != null && event.path != null && event.httpMethod != null) ||
(event.rawPath && event.version === '2.0')
(event.rawPath &&
event.version === '2.0' &&
!(event.requestContext.domainName && awsLambdaFunctionUrlHostRegex.test(event.requestContext.domainName)))
);
}

function isFunctionURLTrigger(event) {
// NOTE: Function URL -> Lambda sets routeKey to $default as a placeholder.
// NOTE: Function URL -> Currently support version 2.0.
// NOTE: Function URL -> Domain name follows the format https://<url-id>.lambda-url.<region>.on.aws
return (
event.routeKey === '$default' &&
event.version === '2.0' &&
event.requestContext.domainName &&
awsLambdaFunctionUrlHostRegex.test(event.requestContext.domainName)
);
}

Expand Down Expand Up @@ -387,3 +408,15 @@ function readSqsStringMessageAttribute(messageAttributes, key) {
function hasFoundTraceCorrelationData(traceCorrelationData) {
return traceCorrelationData.traceId || traceCorrelationData.parentId || traceCorrelationData.level;
}

function extractFunctionUrlEvent(event, span) {
const requestCtx = event.requestContext || {};
const requestCtxHttp = requestCtx.http || {};

span.data.http = {
method: requestCtxHttp.method,
url: requestCtxHttp.path,
params: readHttpQueryParams(event),
header: captureHeaders(event)
};
}
32 changes: 32 additions & 0 deletions packages/aws-lambda/test/integration_test/test_definition.js
Original file line number Diff line number Diff line change
Expand Up @@ -1595,6 +1595,38 @@ function registerTests(handlerDefinitionPath) {
])
));
});
describe('triggered by AWS lambda function url', function () {
const control = prelude.bind(this)({
handlerDefinitionPath,
trigger: 'function-url',
instanaEndpointUrl: backendBaseUrl,
instanaAgentKey
});
it('must recognize the function URL trigger', () =>
verify(
control,
{
error: false,
expectMetrics: true,
expectSpans: true,
trigger: 'aws:lambda.function.url'
},
{
payloadFormatVersion: '2.0'
}
)
.then(() => control.getSpans())
.then(spans =>
expectExactlyOneMatching(
spans,
span => expect(span.n).to.equal('aws.lambda.entry'),
span => expect(span.k).to.equal(constants.ENTRY),
span => expect(span.data.http.method).to.equal('GET'),
span => expect(span.data.http.path).to.equal('/path/to'),
span => expect(span.data.http).to.be.an('object')
)
));
});

function verify(control, expectations, eventOpts) {
return control
Expand Down
27 changes: 26 additions & 1 deletion packages/aws-lambda/test/runtime_mock/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,8 @@ function createEvent(error, trigger, eventOpts = { payloadFormatVersion: '1.0' }
http: {
method: 'POST',
path: '/path/to/path-xxx/path-yyy'
}
},
domainName: 'xxxxxx.execute-api.region.amazonaws.com'
};
event.headers = {
'X-Request-Header-1': 'A Header Value 1, A Header Value 2',
Expand Down Expand Up @@ -588,6 +589,30 @@ function createEvent(error, trigger, eventOpts = { payloadFormatVersion: '1.0' }
];
break;

case 'function-url':
event.version = '2.0';
event.rawQueryString = '';
event.rawPath = '/path/to';
event.routeKey = '$default';
event.requestContext = {
http: {
method: 'GET',
path: '/path/to'
},
domainName: 'xxxxxx.lambda-url.region.on.aws'
};
event.headers = {
'X-Request-Header-1': 'A Header Value 1, A Header Value 2',
'X-Request-Header-2': 'a header value single',
'X-Request-Header-3': 'not configured to capture this'
};
event.queryStringParameters = {
parameter1: 'value1,value2',
parameter2: 'value'
};
addHttpTracingHeaders(event);
break;

default:
throw new Error(`Unknown trigger type: ${trigger}.`);
}
Expand Down

0 comments on commit 6f9fdb2

Please sign in to comment.