├── .gitignore ├── LICENSE ├── README.md ├── WebRTCChat.html ├── connect_handler.py ├── disconnect_handler.py ├── on_message_handler.py ├── package-lock.json ├── package.json ├── python-packages.txt └── serverless.yml /.gitignore: -------------------------------------------------------------------------------- 1 | # Distribution / packaging 2 | .Python 3 | env/ 4 | build/ 5 | develop-eggs/ 6 | dist/ 7 | downloads/ 8 | eggs/ 9 | .eggs/ 10 | lib/ 11 | lib64/ 12 | parts/ 13 | sdist/ 14 | var/ 15 | *.egg-info/ 16 | .installed.cfg 17 | *.egg 18 | .vscode 19 | python 20 | 21 | # Serverless directories 22 | .serverless 23 | 24 | # Node Modules 25 | node_modules/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Suminda Niroshan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS Websocket Messaging 2 | 3 | > This repo contains Serverless Framework project for a simple AWS Websocket chat app 4 | 5 | ### Setup 6 | 7 | > Install required npm packages first 8 | 9 | ```shell 10 | $ npm install 11 | ``` 12 | 13 | > Install Serverless Framework globally 14 | 15 | ```shell 16 | $ npm install -g serverless@1.48.2 17 | ``` 18 | 19 | > Install required Python packages 20 | 21 | ```shell 22 | $ pip install -r python-packages.txt -t ./lib/python 23 | ``` 24 | 25 | > Deploy into AWS 26 | 27 | ```shell 28 | $ serverless deploy --stage dev 29 | ``` -------------------------------------------------------------------------------- /WebRTCChat.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | WebRTC Video Chat 8 | 62 | 63 | 64 | 65 | 66 | 67 |
68 | 69 | 70 |

71 | 72 | 73 |

74 | 75 |

Tap to Change Front/Back Camera

76 | 77 |
78 | 79 | 80 |
81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 |
Outbound Video StatsInbound Video Stats
92 | 93 |
94 |

95 | 96 |

97 | 98 |

99 | 100 | 101 | 102 | 103 | 544 | 545 | -------------------------------------------------------------------------------- /connect_handler.py: -------------------------------------------------------------------------------- 1 | import json 2 | import boto3 3 | import os 4 | import time 5 | 6 | dynamodb = boto3.client('dynamodb') 7 | 8 | def handle(event, context): 9 | connectionId = event['requestContext']['connectionId'] 10 | 11 | # Insert the connectionId of the connected device to the database 12 | dynamodb.put_item(TableName=os.environ['SOCKET_CONNECTIONS_TABLE_NAME'], Item={'connectionId': {'S': connectionId}, 'ttl': {'N': str(int(time.time() + 600))}}) 13 | 14 | return {} -------------------------------------------------------------------------------- /disconnect_handler.py: -------------------------------------------------------------------------------- 1 | import json 2 | import boto3 3 | import os 4 | 5 | dynamodb = boto3.client('dynamodb') 6 | 7 | def handle(event, context): 8 | connectionId = event['requestContext']['connectionId'] 9 | 10 | # Delete connectionId from the database 11 | dynamodb.delete_item(TableName=os.environ['SOCKET_CONNECTIONS_TABLE_NAME'], Key={'connectionId': {'S': connectionId}}) 12 | 13 | return {} -------------------------------------------------------------------------------- /on_message_handler.py: -------------------------------------------------------------------------------- 1 | import json 2 | import boto3 3 | import os 4 | 5 | dynamodb = boto3.client('dynamodb') 6 | 7 | 8 | def handle(event, context): 9 | messageType = json.loads(event['body'])['type'] 10 | messageData = json.loads(event['body'])['data'] 11 | messageId = json.loads(event['body'])['id'] 12 | 13 | paginator = dynamodb.get_paginator('scan') 14 | 15 | connectionIds = [] 16 | 17 | apigatewaymanagementapi = boto3.client('apigatewaymanagementapi', 18 | endpoint_url = "https://" + event["requestContext"]["domainName"] + "/" + event["requestContext"]["stage"]) 19 | 20 | # Retrieve all connectionIds from the database 21 | for page in paginator.paginate(TableName=os.environ['SOCKET_CONNECTIONS_TABLE_NAME']): 22 | connectionIds.extend(page['Items']) 23 | 24 | # Emit the recieved message to all the connected devices 25 | for connectionId in connectionIds: 26 | apigatewaymanagementapi.post_to_connection( 27 | Data=json.dumps({"type": messageType, "data": messageData, "id": messageId}), 28 | ConnectionId=connectionId['connectionId']['S'] 29 | ) 30 | 31 | return {} -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "requires": true, 3 | "lockfileVersion": 1, 4 | "dependencies": { 5 | "serverless-pseudo-parameters": { 6 | "version": "2.4.0", 7 | "resolved": "https://registry.npmjs.org/serverless-pseudo-parameters/-/serverless-pseudo-parameters-2.4.0.tgz", 8 | "integrity": "sha512-lb9R62PUFdEAbbYH7pe1wzR7vtIpa8YI8OVcQ5LlLyE0+AxWG4bwEw33X5LE8+5oLwTy57Y/EevnxKnMeyiXxw==" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "serverless-pseudo-parameters": "2.4.0" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /python-packages.txt: -------------------------------------------------------------------------------- 1 | boto3==1.9.199 -------------------------------------------------------------------------------- /serverless.yml: -------------------------------------------------------------------------------- 1 | service: awswebrtc 2 | 3 | custom: 4 | currentStage: ${opt:stage, self:provider.stage} 5 | lambdaRunTime: python3.7 6 | socketConnectionsTableName: socketConnections-#{AWS::AccountId}-${self:custom.currentStage} 7 | 8 | provider: 9 | name: aws 10 | iamManagedPolicies: 11 | - arn:aws:iam::aws:policy/AmazonDynamoDBFullAccess 12 | runtime: ${self:custom.lambdaRunTime} 13 | versionFunctions: false 14 | region: us-west-2 15 | timeout: 29 16 | 17 | plugins: 18 | - serverless-pseudo-parameters 19 | 20 | package: 21 | individually: true 22 | exclude: 23 | - "lib/**" 24 | - "node_modules/**" 25 | 26 | functions: 27 | connectHandler: 28 | handler: connect_handler.handle 29 | events: 30 | - websocket: $connect 31 | environment: 32 | SOCKET_CONNECTIONS_TABLE_NAME: ${self:custom.socketConnectionsTableName} 33 | 34 | disconnectHandler: 35 | handler: disconnect_handler.handle 36 | events: 37 | - websocket: $disconnect 38 | environment: 39 | SOCKET_CONNECTIONS_TABLE_NAME: ${self:custom.socketConnectionsTableName} 40 | 41 | onMessageHandler: 42 | handler: on_message_handler.handle 43 | events: 44 | - websocket: 45 | route: onMessage 46 | environment: 47 | SOCKET_CONNECTIONS_TABLE_NAME: ${self:custom.socketConnectionsTableName} 48 | 49 | resources: 50 | Resources: 51 | socketConnectionsTable: 52 | Type: AWS::DynamoDB::Table 53 | DeletionPolicy: Retain 54 | Properties: 55 | TableName: ${self:custom.socketConnectionsTableName} 56 | AttributeDefinitions: 57 | - AttributeName: connectionId 58 | AttributeType: S 59 | KeySchema: 60 | - AttributeName: connectionId 61 | KeyType: HASH 62 | BillingMode: PAY_PER_REQUEST 63 | SSESpecification: 64 | SSEEnabled: true 65 | TimeToLiveSpecification: 66 | AttributeName: ttl 67 | Enabled: true --------------------------------------------------------------------------------