Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions docs/events/apigateway.md
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,23 @@ functions:
async: true # default is false
```

### Enabling response streaming

Enable response streaming for proxy integrations by setting `response.transferMode` on your `http` event:

```yml
functions:
stream:
handler: handler.stream
events:
- http:
path: stream
method: get
# Proxy integrations only (AWS_PROXY / HTTP_PROXY)
response:
transferMode: STREAM # defaults to BUFFERED
```

### Catching Exceptions In Your Lambda Function

In case an exception is thrown in your lambda function AWS will send an error message with `Process exited before completing request`. This will be caught by the regular expression for the 500 HTTP status and the 500 status will be returned.
Expand Down
4 changes: 4 additions & 0 deletions docs/guides/serverless.yml.md
Original file line number Diff line number Diff line change
Expand Up @@ -826,6 +826,10 @@ functions:
application/json: '{ "httpMethod" : "$context.httpMethod" }'
# Optional define pass through behavior when content-type does not match any of the specified mapping templates
passThrough: NEVER
# Enable streaming responses by setting transferMode to STREAM (default is BUFFERED)
response:
transferMode: STREAM

```

### Websocket API
Expand Down
3 changes: 3 additions & 0 deletions lib/plugins/aws/package/compile/events/api-gateway/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ const responseSchema = {
additionalProperties: { type: 'string' },
},
template: { type: 'string' },
transferMode: {
anyOf: ['BUFFERED', 'STREAM'].map(caseInsensitive),
},
statusCodes: {
type: 'object',
propertyNames: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ module.exports = {
// * `HTTP_PROXY` for integrating with the HTTP proxy integration, or
// * `AWS_PROXY` for integrating with the Lambda proxy integration type (the default)
if (type === 'AWS' || type === 'AWS_PROXY') {
const responseTransferMode = http.response && http.response.transferMode;
const isStreaming = responseTransferMode && responseTransferMode.toUpperCase() === 'STREAM' && type === 'AWS_PROXY';
const lambdaPathVersion = isStreaming ? '2021-11-15' : '2015-03-31';
const invocationPath = isStreaming ? '/response-streaming-invocations' : '/invocations';

Object.assign(integration, {
Uri: {
'Fn::Join': [
Expand All @@ -79,11 +84,11 @@ module.exports = {
{ Ref: 'AWS::Partition' },
':apigateway:',
{ Ref: 'AWS::Region' },
':lambda:path/2015-03-31/functions/',
`:lambda:path/${lambdaPathVersion}/functions/`,
...[],
{ 'Fn::GetAtt': [lambdaLogicalId, 'Arn'] },
...(lambdaAliasName ? [':', lambdaAliasName] : []),
'/invocations',
invocationPath,
],
],
},
Expand Down Expand Up @@ -124,6 +129,13 @@ module.exports = {
});
}

const responseTransferMode = http.response &&http.response.transferMode;
const supportsResponseTransferMode =
!!responseTransferMode && (type === 'AWS_PROXY' || type === 'HTTP_PROXY');
if (supportsResponseTransferMode) {
Object.assign(integration, { ResponseTransferMode: responseTransferMode });
}

return {
Properties: {
Integration: integration,
Expand Down
58 changes: 50 additions & 8 deletions lib/plugins/aws/package/compile/events/api-gateway/lib/validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,36 @@ module.exports = {

http.integration = this.getIntegration(http);

// Validate transferMode if provided
if (http.response && http.response.transferMode) {
const validTransferModes = ['BUFFERED', 'STREAM'];
const transferMode = http.response.transferMode.toUpperCase();

if (!validTransferModes.includes(transferMode)) {
throw new ServerlessError(
[
`Invalid transferMode "${http.response.transferMode}" in function "${functionName}".`,
`Valid values are: ${validTransferModes.join(', ')}.`,
].join(' '),
'API_GATEWAY_INVALID_TRANSFER_MODE'
);
}

// transferMode is only supported for AWS_PROXY and HTTP_PROXY integrations
if (http.integration !== 'AWS_PROXY' && http.integration !== 'HTTP_PROXY') {
throw new ServerlessError(
[
`transferMode can only be used with AWS_PROXY or HTTP_PROXY integrations.`,
`Function "${functionName}" is using ${http.integration} integration.`,
].join(' '),
'API_GATEWAY_TRANSFER_MODE_UNSUPPORTED_INTEGRATION'
);
}

// Normalize transferMode to uppercase
http.response.transferMode = transferMode;
}

if (http.integration === 'HTTP' || http.integration === 'HTTP_PROXY') {
if (!http.request || !http.request.uri) {
const errorMessage = [
Expand Down Expand Up @@ -171,15 +201,27 @@ module.exports = {
}
}
if (http.response) {
log.warning(
[
`You're using the ${http.integration} in combination with response`,
` configuration in your function "${functionName}".`,
' Serverless will remove this configuration automatically before deployment.',
].join('')
);
const transferMode = http.response.transferMode;
const hasOtherResponseConfig =
Object.keys(http.response).some((key) => key !== 'transferMode');

if (hasOtherResponseConfig) {
log.warning(
[
`You're using the ${http.integration} in combination with response`,
` configuration in your function "${functionName}".`,
' Serverless will remove this configuration automatically before deployment.',
transferMode ? ' (transferMode will be preserved)' : '',
].join('')
);
}

delete http.response;
// Preserve transferMode if present, otherwise delete the entire response object
if (transferMode) {
http.response = { transferMode };
} else {
delete http.response;
}
}
}

Expand Down
1 change: 1 addition & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ export interface AWS {
};
response?: {
contentHandling?: "CONVERT_TO_BINARY" | "CONVERT_TO_TEXT";
transferMode?: "BUFFERED" | "STREAM";
headers?: {
[k: string]: string;
};
Expand Down