internal static Task SendMessage(string apiUrl, string connectionId, string room, JObject message) { Task result = Task.Run(() => { MemoryStream stream = new MemoryStream(UTF8Encoding.UTF8.GetBytes(message.ToString(Newtonsoft.Json.Formatting.None))); AmazonApiGatewayManagementApiConfig config = new AmazonApiGatewayManagementApiConfig() { ServiceURL = apiUrl }; AmazonApiGatewayManagementApiClient client = new AmazonApiGatewayManagementApiClient(config); PostToConnectionRequest postReq = new PostToConnectionRequest() { ConnectionId = connectionId, Data = stream }; try { Logging.LogDebug("Sending to: " + connectionId); Task <PostToConnectionResponse> task = client.PostToConnectionAsync(postReq); task.Wait(); Logging.LogDebug("Sent to: " + connectionId); } catch (Exception ex) { Logging.LogDebug("Deleteing conneciton " + connectionId); error = apiUrl + " - " + connectionId + " - " + ex.ToString(); Connection.Delete(apiUrl, room, connectionId); Logging.LogDebug("Deleted conneciton " + connectionId); } }); return(result); }
public APIGatewayProxyResponse Message(APIGatewayProxyRequest request) { Console.WriteLine("ConnectionId: " + request.RequestContext.ConnectionId); Request finalRequest = JsonConvert.DeserializeObject <Request>(request.Body); Asset asset = new Asset(); Document document = asset.getItem(finalRequest.ContractId); AmazonApiGatewayManagementApiClient client = new AmazonApiGatewayManagementApiClient(new AmazonApiGatewayManagementApiConfig() { ServiceURL = "https://" + request.RequestContext.DomainName + "/" + request.RequestContext.Stage }); MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(document))); PostToConnectionRequest postRequest = new PostToConnectionRequest() { ConnectionId = request.RequestContext.ConnectionId, Data = stream }; var result = client.PostToConnectionAsync(postRequest); result.Wait(); return(new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.OK }); }
public async Task PostToConnection(ConnectionSocketModel connection, MemoryStream stream, ConnectionSocketService connectionService, AmazonApiGatewayManagementApiClient apiClient, ILambdaContext context) { var connectionId = connection.connection_id; context.Logger.LogLine($"Get Connection ID from DB: {connectionId}"); var postConnectionRequest = new PostToConnectionRequest { ConnectionId = connectionId, Data = stream }; try { context.Logger.LogLine($"Post to connection: {connectionId}"); stream.Position = 0; await apiClient.PostToConnectionAsync(postConnectionRequest); } catch (AmazonServiceException e) { if (e.StatusCode == HttpStatusCode.Gone) { connectionService.GetConnection(connectionId); context.Logger.LogLine($"Deleting gone connection: {connectionId}"); } else { context.Logger.LogLine($"Error posting message to {connectionId}: {e.Message}"); context.Logger.LogLine(e.StackTrace); } } }
public void ProcessDynamoEvent(DynamoDBEvent dynamoDBEvent, ILambdaContext context) { AmazonApiGatewayManagementApiClient client = new AmazonApiGatewayManagementApiClient(new AmazonApiGatewayManagementApiConfig() { ServiceURL = "https://g49fepw5h8.execute-api.us-west-2.amazonaws.com/Test" }); AmazonDynamoDBClient amazonDynamoDbClient = new AmazonDynamoDBClient(); Console.WriteLine("Voy a empezar"); Console.WriteLine(dynamoDBEvent.Records); foreach (var dynamoRecord in dynamoDBEvent.Records) { MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(dynamoRecord.Dynamodb.NewImage))); String[] connections = GetConnections(amazonDynamoDbClient, dynamoRecord.Dynamodb.NewImage); foreach (string connection in connections) { PostToConnectionRequest postRequest = new PostToConnectionRequest() { ConnectionId = connection, Data = stream }; var result = client.PostToConnectionAsync(postRequest); result.Wait(); } } }
/// <summary> /// Initiates the asynchronous execution of the PostToConnection operation. /// </summary> /// /// <param name="request">Container for the necessary parameters to execute the PostToConnection operation on AmazonApiGatewayManagementApiClient.</param> /// <param name="callback">An AsyncCallback delegate that is invoked when the operation completes.</param> /// <param name="state">A user-defined state object that is passed to the callback procedure. Retrieve this object from within the callback /// procedure using the AsyncState property.</param> /// /// <returns>An IAsyncResult that can be used to poll or wait for results, or both; this value is also needed when invoking EndPostToConnection /// operation.</returns> /// <seealso href="http://docs.aws.amazon.com/goto/WebAPI/apigatewaymanagementapi-2018-11-29/PostToConnection">REST API Reference for PostToConnection Operation</seealso> public virtual IAsyncResult BeginPostToConnection(PostToConnectionRequest request, AsyncCallback callback, object state) { var options = new InvokeOptions(); options.RequestMarshaller = PostToConnectionRequestMarshaller.Instance; options.ResponseUnmarshaller = PostToConnectionResponseUnmarshaller.Instance; return(BeginInvoke(request, options, callback, state)); }
/// <summary> /// Sends the provided data to the specified connection. /// </summary> /// <param name="request">Container for the necessary parameters to execute the PostToConnection service method.</param> /// <param name="cancellationToken"> /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// </param> /// /// <returns>The response from the PostToConnection service method, as returned by ApiGatewayManagementApi.</returns> /// <exception cref="Amazon.ApiGatewayManagementApi.Model.ForbiddenException"> /// The caller is not authorized to invoke this operation. /// </exception> /// <exception cref="Amazon.ApiGatewayManagementApi.Model.GoneException"> /// The connection with the provided id no longer exists. /// </exception> /// <exception cref="Amazon.ApiGatewayManagementApi.Model.LimitExceededException"> /// The client is sending more than the allowed number of requests per unit of time or /// the WebSocket client side buffer is full. /// </exception> /// <exception cref="Amazon.ApiGatewayManagementApi.Model.PayloadTooLargeException"> /// The data has exceeded the maximum size allowed. /// </exception> /// <seealso href="http://docs.aws.amazon.com/goto/WebAPI/apigatewaymanagementapi-2018-11-29/PostToConnection">REST API Reference for PostToConnection Operation</seealso> public virtual Task <PostToConnectionResponse> PostToConnectionAsync(PostToConnectionRequest request, System.Threading.CancellationToken cancellationToken = default(CancellationToken)) { var options = new InvokeOptions(); options.RequestMarshaller = PostToConnectionRequestMarshaller.Instance; options.ResponseUnmarshaller = PostToConnectionResponseUnmarshaller.Instance; return(InvokeAsync <PostToConnectionResponse>(request, options, cancellationToken)); }
/// <summary> /// Sends the provided data to the specified connection. /// </summary> /// <param name="request">Container for the necessary parameters to execute the PostToConnection service method.</param> /// /// <returns>The response from the PostToConnection service method, as returned by ApiGatewayManagementApi.</returns> /// <exception cref="Amazon.ApiGatewayManagementApi.Model.ForbiddenException"> /// The caller is not authorized to invoke this operation. /// </exception> /// <exception cref="Amazon.ApiGatewayManagementApi.Model.GoneException"> /// The connection with the provided id no longer exists. /// </exception> /// <exception cref="Amazon.ApiGatewayManagementApi.Model.LimitExceededException"> /// The client is sending more than the allowed number of requests per unit of time or /// the WebSocket client side buffer is full. /// </exception> /// <exception cref="Amazon.ApiGatewayManagementApi.Model.PayloadTooLargeException"> /// The data has exceeded the maximum size allowed. /// </exception> /// <seealso href="http://docs.aws.amazon.com/goto/WebAPI/apigatewaymanagementapi-2018-11-29/PostToConnection">REST API Reference for PostToConnection Operation</seealso> public virtual PostToConnectionResponse PostToConnection(PostToConnectionRequest request) { var options = new InvokeOptions(); options.RequestMarshaller = PostToConnectionRequestMarshaller.Instance; options.ResponseUnmarshaller = PostToConnectionResponseUnmarshaller.Instance; return(Invoke <PostToConnectionResponse>(request, options)); }
private async Task <APIGatewayProxyResponse> _broadcast(ScanResponse list, AmazonApiGatewayManagementApiClient apiClient, MemoryStream stream, ILambdaContext context) { var count = 0; foreach (var item in list.Items) { var connectionId = item[Constants.ConnectionIdField].S; var postConnectionRequest = new PostToConnectionRequest { ConnectionId = connectionId, Data = stream }; try { LambdaLogger.Log($"Post to connection {count}: {connectionId}"); stream.Position = 0; await apiClient.PostToConnectionAsync(postConnectionRequest); count++; } catch (AmazonServiceException e) { // API Gateway returns a status of 410 GONE when the connection is no // longer available. If this happens, we simply delete the identifier // from our DynamoDB table. if (e.StatusCode == HttpStatusCode.Gone) { var ddbDeleteRequest = new DeleteItemRequest { TableName = Constants.WEBSOCKET_TABLE, Key = new Dictionary <string, AttributeValue> { { Constants.ConnectionIdField, new AttributeValue { S = connectionId } } } }; context.Logger.LogLine($"Deleting gone connection: {connectionId}"); await _ddbClient.DeleteItemAsync(ddbDeleteRequest); } else { context.Logger.LogLine($"Error posting message to {connectionId}: {e.Message}"); context.Logger.LogLine(e.StackTrace); } } } return(new APIGatewayProxyResponse { StatusCode = 200, Body = "Data send to " + count + " connection" + (count == 1 ? "" : "s") }); }
public async Task FunctionHandler(KinesisEvent kinesisEvent, ILambdaContext context) { context.Logger.LogLine($"Beginning to process {kinesisEvent.Records.Count} records..."); var table = Table.LoadTable(_dynamoDb, Constants.TableName); var scanResp = await _dynamoDb.ScanAsync(new ScanRequest() { TableName = Constants.TableName, ProjectionExpression = Constants.ConnectionIdField }); foreach (var record in kinesisEvent.Records) { context.Logger.LogLine($"Event ID: {record.EventId}"); context.Logger.LogLine($"Event Name: {record.EventName}"); string recordData = GetRecordContents(record.Kinesis); context.Logger.LogLine($"Record Data:"); context.Logger.LogLine(recordData); var stream = new MemoryStream(UTF8Encoding.UTF8.GetBytes(recordData)); foreach (var item in scanResp.Items) { stream.Seek(0, SeekOrigin.Begin); stream.Position = 0; var connectionId = item[Constants.ConnectionIdField].S; var postConnectionRequest = new PostToConnectionRequest() { ConnectionId = connectionId, Data = stream }; try { await _apiClient.PostToConnectionAsync(postConnectionRequest); } catch (AmazonServiceException e) { if (e.StatusCode == HttpStatusCode.Gone) { await table.DeleteItemAsync(connectionId); } else { throw; } } } } context.Logger.LogLine("Stream processing complete."); }
private async Task <APIGatewayProxyResponse> _broadcast(List <WSConnection> list, AmazonApiGatewayManagementApiClient client, MemoryStream stream) { var count = 0; foreach (var item in list) { var connectionId = item.connectionId; var postConnectionRequest = new PostToConnectionRequest { ConnectionId = connectionId, Data = stream }; try { LambdaLogger.Log($"Post to connection {count}: {connectionId}"); stream.Position = 0; await client.PostToConnectionAsync(postConnectionRequest); count++; } catch (AmazonServiceException e) { LambdaLogger.Log($"Connection had appeared to have a problem! " + e.StatusCode); // API Gateway returns a status of 410 GONE when the connection is no // longer available. If this happens, we simply delete the identifier // from our DynamoDB table. if (e.StatusCode == HttpStatusCode.Gone) { var wsConnection = new WSConnection(); wsConnection.connectionId = connectionId; LambdaLogger.Log($"Deleting gone connection: {connectionId}"); await wsdm.deleteSubcriber(wsConnection); } else { var wsConnection = new WSConnection(); wsConnection.connectionId = connectionId; LambdaLogger.Log($"Deleting invalid connection: {connectionId}"); await wsdm.deleteSubcriber(wsConnection); LambdaLogger.Log(e.StackTrace); //LambdaLogger.Log($"Error posting message to {connectionId}: {e.Message}"); } } } return(new APIGatewayProxyResponse { StatusCode = 200, Body = "Connected." }); }
internal static void SendCatchup(string serviceUrl, string connectionId, string room, JArray messages) { JObject message = new JObject() { { "action", "catchup" }, { "room", room }, { "messages", messages } }; MemoryStream stream = new MemoryStream(UTF8Encoding.UTF8.GetBytes(message.ToString(Newtonsoft.Json.Formatting.None))); AmazonApiGatewayManagementApiConfig config = new AmazonApiGatewayManagementApiConfig() { ServiceURL = serviceUrl }; AmazonApiGatewayManagementApiClient client = new AmazonApiGatewayManagementApiClient(config); PostToConnectionRequest postReq = new PostToConnectionRequest() { ConnectionId = connectionId, Data = stream }; client.PostToConnectionAsync(postReq); }
public static void SendPosition(ProxyRequestContext context, string contractId) { Asset asset = new Asset(); Document document = asset.getItem(contractId); AmazonApiGatewayManagementApiClient client = new AmazonApiGatewayManagementApiClient(new AmazonApiGatewayManagementApiConfig() { ServiceURL = "https://" + context.DomainName + "/" + context.Stage }); MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(document))); PostToConnectionRequest postRequest = new PostToConnectionRequest() { ConnectionId = context.ConnectionId, Data = stream }; var result = client.PostToConnectionAsync(postRequest); result.Wait(); }
private async Task SendJson(ChatSession session, object json) { var request = new PostToConnectionRequest { ConnectionId = session.ConnectionId, Data = new MemoryStream(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(json))) }; try { await gateway.PostToConnectionAsync(request); } catch (GoneException) { // Delete this connection if it wasn't updated var deleteRequest = new DeleteItemRequest { Key = ChatSession.ToKey(session), TableName = SessionTable, ConditionExpression = "#Connection = :connectionId", ExpressionAttributeNames = new Dictionary <string, string> { { "#Connection", ChatSession.ConnectionAttribute } }, ExpressionAttributeValues = new Dictionary <string, AttributeValue> { { ":connectionId", new AttributeValue(session.ConnectionId) } } }; try { await dynamo.DeleteItemAsync(deleteRequest); } catch (ConditionalCheckFailedException) { // Do nothing, the connection was since updated } } }
public async void NotifyMessageUpdates_SendsProperMessage() { var message = new Message { ClientId = "foo", MsgText = "bar" }; var update = CreateDynamoEvent(message); var expectedWebSocketConnections = new List <WebSocketConnection> { new WebSocketConnection { ConnectionId = "some connection id" } }; var asyncSearch = new Mock <IAsyncSearchWrapper <WebSocketConnection> >(); asyncSearch.Setup(x => x.GetRemainingAsync(It.IsAny <CancellationToken>())) .ReturnsAsync(expectedWebSocketConnections); _dynamoDbContextWrapper.Setup(x => x.QueryAsync <WebSocketConnection>("true", null)) .Returns(asyncSearch.Object); PostToConnectionRequest postRequest = null; _apiGatewayManagementApi.Setup(x => x.PostToConnectionAsync( It.IsAny <PostToConnectionRequest>(), It.IsAny <CancellationToken>())) .Callback <PostToConnectionRequest, CancellationToken>((r, _) => postRequest = r) .ReturnsAsync(new PostToConnectionResponse()); await _testObject.NotifyMessageUpdate(update, new TestLambdaContext()); var data = Encoding.UTF8.GetString(postRequest.Data.ToArray()); Assert.Equal(JsonConvert.SerializeObject(message, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }), data); }
public async Task <APIGatewayProxyResponse> SendMessageHandler(APIGatewayProxyRequest request, ILambdaContext context) { try { // Construct the API Gateway endpoint that incoming message will be broadcasted to. var domainName = request.RequestContext.DomainName; var stage = request.RequestContext.Stage; var endpoint = $"https://{domainName}/{stage}"; context.Logger.LogLine($"API Gateway management endpoint: {endpoint}"); // The body will look something like this: {"message":"sendmessage", "data":"What are you doing?"} JsonDocument message = JsonDocument.Parse(request.Body); // Grab the data from the JSON body which is the message to broadcasted. JsonElement dataProperty; if (!message.RootElement.TryGetProperty("data", out dataProperty)) { context.Logger.LogLine("Failed to find data element in JSON document"); return(new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.BadRequest }); } var data = dataProperty.GetString(); var stream = new MemoryStream(UTF8Encoding.UTF8.GetBytes(data)); // List all of the current connections. In a more advanced use case the table could be used to grab a group of connection ids for a chat group. var scanRequest = new ScanRequest { TableName = ConnectionMappingTable, ProjectionExpression = ConnectionIdField }; var scanResponse = await DDBClient.ScanAsync(scanRequest); // Construct the IAmazonApiGatewayManagementApi which will be used to send the message to. var apiClient = ApiGatewayManagementApiClientFactory(endpoint); // Loop through all of the connections and broadcast the message out to the connections. var count = 0; foreach (var item in scanResponse.Items) { var postConnectionRequest = new PostToConnectionRequest { ConnectionId = item[ConnectionIdField].S, Data = stream }; try { context.Logger.LogLine($"Post to connection {count}: {postConnectionRequest.ConnectionId}"); stream.Position = 0; await apiClient.PostToConnectionAsync(postConnectionRequest); count++; } catch (AmazonServiceException e) { // API Gateway returns a status of 410 GONE then the connection is no // longer available. If this happens, delete the identifier // from our DynamoDB table. if (e.StatusCode == HttpStatusCode.Gone) { var ddbDeleteRequest = new DeleteItemRequest { TableName = ConnectionMappingTable, Key = new Dictionary <string, AttributeValue> { { ConnectionIdField, new AttributeValue { S = postConnectionRequest.ConnectionId } } } }; context.Logger.LogLine($"Deleting gone connection: {postConnectionRequest.ConnectionId}"); await DDBClient.DeleteItemAsync(ddbDeleteRequest); } else { context.Logger.LogLine($"Error posting message to {postConnectionRequest.ConnectionId}: {e.Message}"); context.Logger.LogLine(e.StackTrace); } } } return(new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.OK, Body = "Data sent to " + count + " connection" + (count == 1 ? "" : "s") }); } catch (Exception e) { context.Logger.LogLine("Error disconnecting: " + e.Message); context.Logger.LogLine(e.StackTrace); return(new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.InternalServerError, Body = $"Failed to send message: {e.Message}" }); } }
public async Task <APIGatewayProxyResponse> FunctionHandler(APIGatewayProxyRequest request, ILambdaContext context) { LambdaLogger.Log(JObject.FromObject(request).ToString()); try { var domainName = request.RequestContext.DomainName; var stage = request.RequestContext.Stage; var endpoint = $"https://{domainName}/{stage}"; LambdaLogger.Log("API Gateway management endpoint:" + endpoint); var message = JsonConvert.DeserializeObject <JObject>(request.Body); var data = message["data"]?.ToString(); var stream = new MemoryStream(UTF8Encoding.UTF8.GetBytes(data)); var scanRequest = new ScanRequest { TableName = Environment.GetEnvironmentVariable("DynamoChatTable"), ProjectionExpression = "ConnectionId" }; var scanResponse = await ddbClient.ScanAsync(scanRequest); var apiClient = new AmazonApiGatewayManagementApiClient(new AmazonApiGatewayManagementApiConfig { ServiceURL = endpoint }); var count = 0; foreach (var item in scanResponse.Items) { var connectionId = item["ConnectionId"].S; var postConnectionRequest = new PostToConnectionRequest { ConnectionId = connectionId, Data = stream }; try { context.Logger.LogLine($"Post to connection {count}: {connectionId}"); stream.Position = 0; await apiClient.PostToConnectionAsync(postConnectionRequest); count++; } catch (AmazonServiceException e) { // API Gateway returns a status of 410 GONE when the connection is no // longer available. If this happens, we simply delete the identifier // from our DynamoDB table. if (e.StatusCode == HttpStatusCode.Gone) { var ddbDeleteRequest = new DeleteItemRequest { TableName = Environment.GetEnvironmentVariable("DynamoChatTable"), Key = new Dictionary <string, AttributeValue> { { "ConnectionId", new AttributeValue { S = connectionId } } } }; context.Logger.LogLine($"Deleting gone connection: {connectionId}"); await ddbClient.DeleteItemAsync(ddbDeleteRequest); } else { context.Logger.LogLine($"Error posting message to {connectionId}: {e.Message}"); context.Logger.LogLine(e.StackTrace); } } } return(new APIGatewayProxyResponse { StatusCode = 200, Body = "Data send to " + count + " connection" + (count == 1 ? "" : "s") }); } catch (Exception e) { LambdaLogger.Log("Error disconnecting: " + e.Message); LambdaLogger.Log(e.StackTrace); return(new APIGatewayProxyResponse { StatusCode = 500, Body = $"Failed to send message: {e.Message}" }); } }
public async Task <APIGatewayProxyResponse> FunctionHandler(APIGatewayProxyRequest request, ILambdaContext context) { try { var domainName = request.RequestContext.DomainName; var stage = request.RequestContext.Stage; var endpoint = $"https://{domainName}/{stage}"; context.Logger.LogLine($"API Gateway management endpoint: {endpoint}"); var message = JsonConvert.DeserializeObject <JObject>(request.Body); var data = message["data"]?.ToString(); var stream = new MemoryStream(UTF8Encoding.UTF8.GetBytes(data)); var scanRequest = new ScanRequest { TableName = Constants.TABLE_NAME, ProjectionExpression = Constants.ConnectionIdField }; var scanResponse = await _ddbClient.ScanAsync(scanRequest); var apiClient = new AmazonApiGatewayManagementApiClient(new AmazonApiGatewayManagementApiConfig { ServiceURL = endpoint }); var count = 0; foreach (var item in scanResponse.Items) { var connectionId = item[Constants.ConnectionIdField].S; var postConnectionRequest = new PostToConnectionRequest { ConnectionId = connectionId, Data = stream }; try { context.Logger.LogLine($"Post to connection {count}: {connectionId}"); stream.Position = 0; await apiClient.PostToConnectionAsync(postConnectionRequest); count++; } catch (AmazonServiceException e) { if (e.StatusCode == HttpStatusCode.Gone) { var ddbDeleteRequest = new DeleteItemRequest { TableName = Constants.TABLE_NAME, Key = new Dictionary <string, AttributeValue> { { Constants.ConnectionIdField, new AttributeValue { S = connectionId } } } }; context.Logger.LogLine($"Deleting gone connection: {connectionId}"); await _ddbClient.DeleteItemAsync(ddbDeleteRequest); } else { context.Logger.LogLine($"Error posting message to {connectionId}: {e.Message}"); context.Logger.LogLine(e.StackTrace); } } } return(new APIGatewayProxyResponse { StatusCode = 200, Body = "Data send to " + count + " connection" + (count == 1 ? "" : "s") }); } catch (Exception e) { context.Logger.LogLine("Error disconnecting: " + e.Message); context.Logger.LogLine(e.StackTrace); return(new APIGatewayProxyResponse { StatusCode = 500, Body = $"Failed to send message: {e.Message}" }); } }
public async Task <APIGatewayProxyResponse> Handler(APIGatewayProxyRequest input, ILambdaContext context) { var client = new AmazonDynamoDBClient(); var scanRequest = new ScanRequest { TableName = Environment.ExpandEnvironmentVariables("%TABLE_NAME%"), ProjectionExpression = "connectionId" }; ScanResponse connections = null; try { connections = await client.ScanAsync(scanRequest); } catch (Exception e) { return(new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.InternalServerError, Body = e.Message, Headers = new Dictionary <string, string> { { "Content-Type", "text/plain" } }, }); } var data = JObject.Parse(input.Body)["data"].ToString(); var byteArray = Encoding.UTF8.GetBytes(data); var config = new AmazonApiGatewayManagementApiConfig { ServiceURL = $"https://{input.RequestContext.DomainName}/{input.RequestContext.Stage}" }; var apiClient = new AmazonApiGatewayManagementApiClient(config); var connectionIds = connections.Items.Select(item => item["connectionId"].S).ToList(); foreach (var connectionId in connectionIds) { var postData = new MemoryStream(byteArray); try { var postToRequest = new PostToConnectionRequest { ConnectionId = connectionId, Data = postData }; await apiClient.PostToConnectionAsync(postToRequest); } catch (GoneException) { Console.WriteLine($"Found dead connection, deleting {connectionId}"); var attributes = new Dictionary <string, AttributeValue>(); attributes["connectionId"] = new AttributeValue { S = connectionId }; var deleteRequest = new DeleteItemRequest { TableName = Environment.ExpandEnvironmentVariables("%TABLE_NAME%"), Key = attributes }; try { await client.DeleteItemAsync(deleteRequest); } catch (Exception e) { return(new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.InternalServerError, Body = e.Message, Headers = new Dictionary <string, string> { { "Content-Type", "text/plain" } }, }); } } catch (Exception e) { return(new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.InternalServerError, Body = e.Message, Headers = new Dictionary <string, string> { { "Content-Type", "text/plain" } }, }); } } return(new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.OK, Body = "data sent", Headers = new Dictionary <string, string> { { "Content-Type", "text/plain" } }, }); }
public async Task <APIGatewayProxyResponse> PingPongHandler(APIGatewayProxyRequest request, ILambdaContext context) { try { var domainName = request.RequestContext.DomainName; var stage = request.RequestContext.Stage; var endpoint = $"https://{domainName}/{stage}"; var apiClient = ApiGatewayManagementApiClientFactory(endpoint); context.Logger.LogLine($"API Gateway management endpoint: {endpoint}"); var connectionId = request.RequestContext.ConnectionId; context.Logger.LogLine($"Ping from ConnectionId: {connectionId}"); var stream = new MemoryStream(Encoding.UTF8.GetBytes("{\"message\": \"pong\"}")); var postConnectionRequest = new PostToConnectionRequest { ConnectionId = connectionId, Data = stream }; try { context.Logger.LogLine($"Reply pong to connection: {postConnectionRequest.ConnectionId}"); stream.Position = 0; await apiClient.PostToConnectionAsync(postConnectionRequest); } catch (AmazonServiceException e) { // API Gateway returns a status of 410 GONE then the connection is no // longer available. If this happens, delete the identifier // from our DynamoDB table. if (e.StatusCode == HttpStatusCode.Gone) { var ddbDeleteRequest = new DeleteItemRequest { TableName = ConnectionMappingTable, Key = new Dictionary <string, AttributeValue> { { ConnectionIdField, new AttributeValue { S = postConnectionRequest.ConnectionId } } } }; context.Logger.LogLine($"Deleting gone connection: {postConnectionRequest.ConnectionId}"); await DdbClient.DeleteItemAsync(ddbDeleteRequest); } else { context.Logger.LogLine($"Error posting message to {postConnectionRequest.ConnectionId}: {e.Message}"); context.Logger.LogLine(e.StackTrace); } } return(new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.OK, Body = $"Pong sent to connection id {connectionId}" }); } catch (Exception e) { context.Logger.LogLine("Error ping/pong: " + e.Message); context.Logger.LogLine(e.StackTrace); return(new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.InternalServerError, Body = $"Failed to send message: {e.Message}" }); } }
public async Task <APIGatewayProxyResponse> SendMessageHandler(APIGatewayProxyRequest request, ILambdaContext context) { try { // Construct the API Gateway endpoint that incoming message will be broadcasted to. var domainName = request.RequestContext.DomainName; var stage = request.RequestContext.Stage; var endpoint = $"https://{domainName}/{stage}"; var connectionId = request.RequestContext.ConnectionId; context.Logger.LogLine($"API Gateway management endpoint: {endpoint}"); JsonDocument message = JsonDocument.Parse(request.Body); // Grab the data from the JSON body which is the message to broadcasted. JsonElement dataProperty; if (!message.RootElement.TryGetProperty("data", out dataProperty)) { context.Logger.LogLine("Failed to find data element in JSON document"); return(new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.BadRequest }); } var options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true, }; ChatMessageRequest messageRequest = JsonSerializer.Deserialize <ChatMessageRequest>(dataProperty.ToString(), options); string date = (DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds.ToString(); await DDBUtils.PutMessage(messageRequest.Message, messageRequest.RoomID, messageRequest.UserID, date); ChatMessageResponse chatMsg = new ChatMessageResponse { Success = true, Message = messageRequest.Message, Date = date, Author = messageRequest.UserID, RoomId = messageRequest.RoomID }; string data = JsonSerializer.Serialize(chatMsg); var stream = new MemoryStream(UTF8Encoding.UTF8.GetBytes(data)); var queryRequest = new QueryRequest { TableName = RoomsConnectionsTable, KeyConditionExpression = "RoomId = :ri", ExpressionAttributeValues = new Dictionary <string, AttributeValue> { { ":ri", new AttributeValue { S = messageRequest.RoomID } } }, ProjectionExpression = "RoomId, ConnectionId" }; var queryResponse = await DDBClient.QueryAsync(queryRequest); // Construct the IAmazonApiGatewayManagementApi which will be used to send the message to. var apiClient = ApiGatewayManagementApiClientFactory(endpoint); // Loop through all of the connections and broadcast the message out to the connections. var count = 0; foreach (var item in queryResponse.Items) { var postConnectionRequest = new PostToConnectionRequest { ConnectionId = item["ConnectionId"].S, Data = stream }; try { context.Logger.LogLine($"Post to connection {count}: {postConnectionRequest.ConnectionId}"); stream.Position = 0; await apiClient.PostToConnectionAsync(postConnectionRequest); count++; } catch (AmazonServiceException e) { // API Gateway returns a status of 410 GONE then the connection is no // longer available. If this happens, delete the identifier // from our DynamoDB table. if (e.StatusCode == HttpStatusCode.Gone) { var ddbDeleteRequest = new DeleteItemRequest { TableName = RoomsConnectionsTable, Key = new Dictionary <string, AttributeValue> { { "RoomId", new AttributeValue { S = item["RoomId"].S } }, { "ConnectionId", new AttributeValue { S = postConnectionRequest.ConnectionId } } } }; context.Logger.LogLine($"Deleting gone connection: {postConnectionRequest.ConnectionId}"); await DDBClient.DeleteItemAsync(ddbDeleteRequest); } else { context.Logger.LogLine($"Error posting message to {postConnectionRequest.ConnectionId}: {e.Message}"); context.Logger.LogLine(e.StackTrace); } } } return(new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.OK, Body = "Data sent to " + count + " connection" + (count == 1 ? "" : "s") }); } catch (Exception e) { context.Logger.LogLine("Error sending message: " + e.Message); context.Logger.LogLine(e.StackTrace); return(new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.InternalServerError, Body = $"Failed to send message: {e.Message}" }); } }
public async Task SendMessage(MessageEvent evnt) { if (string.IsNullOrEmpty(_ddbTableName)) { return; } var payload = JsonConvert.SerializeObject(evnt); var stream = new MemoryStream(Encoding.UTF8.GetBytes(payload)); QueryResponse queryResponse; if (!_connectionsCache.TryGetValue(evnt.TargetUser, out ICacheEntry entry) || entry.AbsoluteExpiration < DateTime.UtcNow) { var queryRequest = new QueryRequest { TableName = _ddbTableName, IndexName = "username", KeyConditionExpression = $"{UsernameField} = :u", ExpressionAttributeValues = new Dictionary <string, AttributeValue> { { ":u", new AttributeValue { S = evnt.TargetUser } } } }; queryResponse = await _ddbClient.QueryAsync(queryRequest); entry = _connectionsCache.CreateEntry(evnt.TargetUser); entry.AbsoluteExpiration = DateTime.UtcNow.AddSeconds(10); entry.SetValue(queryResponse); _connectionsCache.Set(evnt.TargetUser, entry); } else { queryResponse = entry.Value as QueryResponse; } AmazonApiGatewayManagementApiClient apiClient = null; try { var goneConnections = new List <Dictionary <string, AttributeValue> >(); foreach (var item in queryResponse.Items) { var endpoint = item[EndpointField].S; if (apiClient == null || !apiClient.Config.ServiceURL.Equals(endpoint, StringComparison.Ordinal)) { if (apiClient != null) { apiClient.Dispose(); apiClient = null; } apiClient = new AmazonApiGatewayManagementApiClient(new AmazonApiGatewayManagementApiConfig { ServiceURL = endpoint }); } var connectionId = item[ConnectionIdField].S; stream.Position = 0; var postConnectionRequest = new PostToConnectionRequest { ConnectionId = connectionId, Data = stream }; try { await apiClient.PostToConnectionAsync(postConnectionRequest); } catch (GoneException) { goneConnections.Add(item); } } // Remove connections from the cache that have disconnected. foreach (var goneConnectionItem in goneConnections) { queryResponse.Items.Remove(goneConnectionItem); } } catch { // Never stop rendering based on communication errors. } finally { apiClient?.Dispose(); } }