mirror of
https://github.com/serverless/serverless.git
synced 2026-01-18 14:58:43 +00:00
feat(AWS HTTP API): Support timeout configuration
This commit is contained in:
parent
2de15462e7
commit
df9846d9af
@ -64,6 +64,32 @@ functions:
|
||||
path: /get/for/any/{param}
|
||||
```
|
||||
|
||||
### Endpoints timeout
|
||||
|
||||
By default HTTP API will timeout within 5 seconds. Timeout can be restricted to as low value as 50 milliseconds or lifted up to 29 seconds.
|
||||
|
||||
To adjust timeout for all configured endpoints, outline desired value in `provider` settings:
|
||||
|
||||
```yaml
|
||||
provider:
|
||||
httpApi:
|
||||
timeout: 0.5 # Restrict endpoints to timeout in 500ms
|
||||
```
|
||||
|
||||
To adjust timeout for specific endpoint, outline it at `httpApi` event configuration:
|
||||
|
||||
```yaml
|
||||
functions:
|
||||
withCustomTimeout:
|
||||
handler: handler.withCustomTimeout
|
||||
events:
|
||||
- httpApi:
|
||||
...
|
||||
timeout: 29
|
||||
```
|
||||
|
||||
**Note**: All `httpApi` events for same function should share same timeout setting.
|
||||
|
||||
### CORS Setup
|
||||
|
||||
With HTTP API we may configure CORS headers that'll be effective for all configured endpoints.
|
||||
|
||||
@ -79,6 +79,7 @@ provider:
|
||||
targetGroupPrefix: xxxxxxxxxx # Optional prefix to prepend when generating names for target groups
|
||||
httpApi:
|
||||
id: # If we want to attach to externally created HTTP API its id should be provided here
|
||||
timeout: 5 # Timeout setting for all endpoints, defaults to 5s, can be set to values ranging from 0.05s to 29s
|
||||
cors: true # Implies default behavior, can be fine tuned with specficic options
|
||||
authorizers:
|
||||
# JWT authorizers to back HTTP API endpoints
|
||||
@ -239,6 +240,9 @@ functions:
|
||||
- httpApi: # HTTP API endpoint
|
||||
method: GET
|
||||
path: /some-get-path/{param}
|
||||
# Timeout setting for given endpoint. Defaults to 5s, can be set to values from 0.05s to 29s
|
||||
# Note: All httpApi events for same function need to share same timeout setting
|
||||
timeout: 5
|
||||
authorizer: # Optional
|
||||
name: someJwtAuthorizer # References by name authorizer defined in provider.httpApi.authorizers section
|
||||
scopes: # Optional
|
||||
|
||||
@ -258,8 +258,9 @@ Object.defineProperties(
|
||||
let method;
|
||||
let path;
|
||||
let authorizer;
|
||||
let timeout;
|
||||
if (_.isObject(event.httpApi)) {
|
||||
({ method, path, authorizer } = event.httpApi);
|
||||
({ method, path, authorizer, timeout } = event.httpApi);
|
||||
} else {
|
||||
const methodPath = String(event.httpApi);
|
||||
if (methodPath === '*') {
|
||||
@ -353,6 +354,17 @@ Object.defineProperties(
|
||||
routeConfig.authorizer = authorizers.get(name);
|
||||
if (scopes) routeConfig.authorizationScopes = toSet(scopes);
|
||||
}
|
||||
if (!timeout) timeout = userConfig.timeout || null;
|
||||
if (typeof routeTargetData.timeout !== 'undefined') {
|
||||
if (routeTargetData.timeout !== timeout) {
|
||||
throw new this.serverless.classes.Error(
|
||||
`Inconsistent timeout settings for ${functionName} events`,
|
||||
'INCONSISTENT_HTTP_API_TIMEOUT'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
routeTargetData.timeout = timeout;
|
||||
}
|
||||
routes.set(routeKey, routeConfig);
|
||||
if (shouldFillCorsMethods) {
|
||||
if (event.resolvedMethod === 'ANY') {
|
||||
@ -367,16 +379,20 @@ Object.defineProperties(
|
||||
}
|
||||
}),
|
||||
compileIntegration: d(function(routeTargetData) {
|
||||
const properties = {
|
||||
ApiId: this.getApiIdConfig(),
|
||||
IntegrationType: 'AWS_PROXY',
|
||||
IntegrationUri: resolveTargetConfig(routeTargetData),
|
||||
PayloadFormatVersion: '1.0',
|
||||
};
|
||||
if (routeTargetData.timeout) {
|
||||
properties.TimeoutInMillis = Math.round(routeTargetData.timeout * 1000);
|
||||
}
|
||||
this.cfTemplate.Resources[
|
||||
this.provider.naming.getHttpApiIntegrationLogicalId(routeTargetData.functionName)
|
||||
] = {
|
||||
Type: 'AWS::ApiGatewayV2::Integration',
|
||||
Properties: {
|
||||
ApiId: this.getApiIdConfig(),
|
||||
IntegrationType: 'AWS_PROXY',
|
||||
IntegrationUri: resolveTargetConfig(routeTargetData),
|
||||
PayloadFormatVersion: '1.0',
|
||||
},
|
||||
Properties: properties,
|
||||
};
|
||||
}),
|
||||
compileLambdaPermissions: d(function(routeTargetData) {
|
||||
|
||||
@ -452,4 +452,47 @@ describe('HttpApiEvents', () => {
|
||||
expect(resource.Properties.Action).to.equal('lambda:InvokeFunction');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Timeout', () => {
|
||||
let cfResources;
|
||||
let naming;
|
||||
|
||||
before(() =>
|
||||
fixtures
|
||||
.extend('httpApi', {
|
||||
provider: { httpApi: { timeout: 3 } },
|
||||
functions: {
|
||||
other: {
|
||||
events: [
|
||||
{
|
||||
httpApi: {
|
||||
timeout: 20.56,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
})
|
||||
.then(fixturePath =>
|
||||
runServerless({
|
||||
cwd: fixturePath,
|
||||
cliArgs: ['package'],
|
||||
}).then(serverless => {
|
||||
({
|
||||
Resources: cfResources,
|
||||
} = serverless.service.provider.compiledCloudFormationTemplate);
|
||||
naming = serverless.getProvider('aws').naming;
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
it('Should support timeout set at endpoint', () => {
|
||||
const resource = cfResources[naming.getHttpApiIntegrationLogicalId('other')];
|
||||
expect(resource.Properties.TimeoutInMillis).to.equal(20560);
|
||||
});
|
||||
it('Should support globally set timeout', () => {
|
||||
const resource = cfResources[naming.getHttpApiIntegrationLogicalId('foo')];
|
||||
expect(resource.Properties.TimeoutInMillis).to.equal(3000);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
20
tests/fixtures/httpApi/index.js
vendored
20
tests/fixtures/httpApi/index.js
vendored
@ -1,10 +1,14 @@
|
||||
'use strict';
|
||||
|
||||
module.exports.handler = (event, context, callback) =>
|
||||
callback(null, {
|
||||
statusCode: 200,
|
||||
body: JSON.stringify({
|
||||
path: event.path,
|
||||
method: event.httpMethod,
|
||||
}),
|
||||
});
|
||||
module.exports.handler = (event, context, callback) => {
|
||||
const resolve = () =>
|
||||
callback(null, {
|
||||
statusCode: 200,
|
||||
body: JSON.stringify({
|
||||
path: event.path,
|
||||
method: event.httpMethod,
|
||||
}),
|
||||
});
|
||||
if (event.path === '/bar/timeout') setTimeout(resolve, 2000);
|
||||
else resolve();
|
||||
};
|
||||
|
||||
@ -89,6 +89,15 @@ describe('HTTP API Integration Test', function() {
|
||||
},
|
||||
],
|
||||
},
|
||||
other: {
|
||||
events: [
|
||||
{
|
||||
httpApi: {
|
||||
timeout: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
@ -136,6 +145,13 @@ describe('HTTP API Integration Test', function() {
|
||||
expect(response.status).to.equal(404);
|
||||
});
|
||||
|
||||
it('should respect timeout settings', async () => {
|
||||
const testEndpoint = `${endpoint}/bar/timeout`;
|
||||
|
||||
const response = await fetch(testEndpoint, { method: 'GET' });
|
||||
expect(response.status).to.equal(503);
|
||||
});
|
||||
|
||||
it('should support CORS when indicated', async () => {
|
||||
const testEndpoint = `${endpoint}/bar/whatever`;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user