Skip to content

Instantly share code, notes, and snippets.

@mikestaub
Created July 26, 2024 04:08
Show Gist options
  • Save mikestaub/365762ea4a91aa756c2566c307d67054 to your computer and use it in GitHub Desktop.
Save mikestaub/365762ea4a91aa756c2566c307d67054 to your computer and use it in GitHub Desktop.
cloudformation template to demo AWS lambda with SSE
AWSTemplateFormatVersion: '2010-09-09'
Description: 'CloudFormation template for SSE with Lambda Function URL'
Resources:
LambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: LambdaExecutionPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: arn:aws:logs:*:*:*
SSELambdaFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: SSETestFunction
Handler: index.handler
Role: !GetAtt LambdaExecutionRole.Arn
Code:
ZipFile: |
exports.handler = awslambda.streamifyResponse(async (event, responseStream, context) => {
console.log('Event:', JSON.stringify(event, null, 2));
console.log('Headers:', JSON.stringify(event.headers, null, 2));
const headers = {
'Access-Control-Allow-Origin': 'http://localhost:3000',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Accept, Accept-Language, Content-Type, Origin, Referer, User-Agent',
'Access-Control-Max-Age': '86400',
'Vary': 'Origin'
};
if (event.requestContext.http.method === 'OPTIONS') {
// Handle CORS preflight request
const response = {
statusCode: 204,
headers: headers,
body: ''
};
responseStream.write(JSON.stringify(response));
responseStream.end();
return;
}
const httpResponseMetadata = {
statusCode: 200,
headers: {
...headers,
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
}
};
responseStream = awslambda.HttpResponseStream.from(responseStream, httpResponseMetadata);
const generateEvents = async function* () {
for (let i = 0; i < 5; i++) {
yield `event: message\ndata: ${JSON.stringify({message: `Event ${i}`})}\n\n`;
await new Promise(resolve => setTimeout(resolve, 1000));
}
};
try {
for await (const chunk of generateEvents()) {
responseStream.write(chunk);
}
} catch (error) {
console.error('Error:', error);
responseStream.write('event: error\ndata: Internal Server Error\n\n');
} finally {
responseStream.end();
}
});
Runtime: nodejs20.x
Timeout: 60
SSELambdaFunctionUrl:
Type: AWS::Lambda::Url
Properties:
AuthType: NONE
TargetFunctionArn: !Ref SSELambdaFunction
InvokeMode: RESPONSE_STREAM
SSELambdaPermission:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !Ref SSELambdaFunction
Action: lambda:InvokeFunctionUrl
Principal: "*"
FunctionUrlAuthType: NONE
Outputs:
LambdaFunctionUrl:
Description: "Lambda Function URL for SSE testing"
Value: !GetAtt SSELambdaFunctionUrl.FunctionUrl
<!DOCTYPE html>
<html>
<body>
<h1>SSE Test</h1>
<div id="events"></div>
<script>
const eventsDiv = document.getElementById('events');
const eventSource = new EventSource('https://REPLACEME.lambda-url.us-east-1.on.aws/');
eventSource.onmessage = function(event) {
const p = document.createElement('p');
p.textContent = event.data;
eventsDiv.appendChild(p);
};
eventSource.onerror = function(error) {
console.error('EventSource failed:', error);
eventSource.close();
};
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment