API Gateway WSS Endpoint not found

0

I've created a WSS chat app using the sample that comes with the AWS dotnet lambda templates. My web front end can connect ok and it creates a record in dynamo but when I try to broadcast a message to all connections I get the following error:

`Name or service not known (execute-api.ap-southeast-2.amazonaws.com:443) `

I'm using the following code to set it:

  var protocol = "https";
  //var protocol = "wss";
  
  var domainName = request.RequestContext.DomainName;
  //var domainName = "ID HERE.execute-api.ap-southeast-2.amazonaws.com";
  
  var stage = request.RequestContext.Stage;
  // var stage = "";
  //var stage = "test";
  //var stage = "test/@connections";
  
  var endpoint = $"{protocol}://{domainName}/{stage}";

and it logs the following:

API Gateway management endpoint: https://ID HERE.execute-api.ap-southeast-2.amazonaws.com/test

Ive tried all the combinations and a custom domain. Im thinking that ap-southeast-2 does not support wss ? Or ... ?? Been stuck on this for a while now. About ready to give up. Anyone got any ideas??

Update: Heres the code for sending the message - it just an updated version of the sample. From the startup:

  public Functions()
  {
    DDBClient = new AmazonDynamoDBClient();

    // Grab the name of the DynamoDB from the environment variable setup in the CloudFormation template serverless.template
    if (Environment.GetEnvironmentVariable(TABLE_NAME_ENV) == null)
    {
      throw new ArgumentException($"Missing required environment variable {TABLE_NAME_ENV}");
    }

    ConnectionMappingTable = Environment.GetEnvironmentVariable(TABLE_NAME_ENV) ?? "";

    this.ApiGatewayManagementApiClientFactory = (Func<string, AmazonApiGatewayManagementApiClient>)((endpoint) =>
    {
      return new AmazonApiGatewayManagementApiClient(new AmazonApiGatewayManagementApiConfig
      {
        ServiceURL = endpoint,
        
        RegionEndpoint = RegionEndpoint.APSoutheast2, // without this I get Credential errors
        LogResponse = true,  // dont see anything extra with these
        LogMetrics = true,
        DisableLogging = false
      });
    });
  }

And the SendMessageFunction:

    try
    {
      // Construct the API Gateway endpoint that incoming message will be broadcasted to.
      var protocol = "https";
      //var protocol = "wss";
      
      var domainName = request.RequestContext.DomainName;
      //var domainName = "?????.execute-api.ap-southeast-2.amazonaws.com";
      
      var stage = request.RequestContext.Stage;
      // var stage = "";
      //var stage = "test";
      //var stage = "test/@connections";
      
      var endpoint = $"{protocol}://{domainName}/{stage}";
      
      context.Logger.LogInformation($"API Gateway management endpoint: {endpoint}");
      
      JObject message = JObject.Parse(request.Body);
      context.Logger.LogInformation(request.Body);


      if (!GetRecipient(message, context, out WSMessageRecipient? recipient))
      {
        context.Logger.LogError($"Invalid or empty WSMessageRecipient");
        return new APIGatewayProxyResponse
        {
          StatusCode = (int)HttpStatusCode.BadRequest, Body = "Nothing to do or invalid request"
        };
      }
      
      if (!GetData(message, context, out string? data))
      {
        context.Logger.LogError($"Invalid or empty WSSendMessage");
        return new APIGatewayProxyResponse
        {
          StatusCode = (int)HttpStatusCode.BadRequest, Body = "Nothing to do or invalid request"
        };
      }

      var stream = new MemoryStream(UTF8Encoding.UTF8.GetBytes(data!));
      
      if (stream.Length == 0)
      {
        context.Logger.LogError($"Empty Stream");
        return new APIGatewayProxyResponse
        {
          StatusCode = (int)HttpStatusCode.BadRequest, Body = "Empty data stream"
        };
      }

      // 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.
      ScanResponse scanResponse = await GetConnectionItems(recipient);

      // Construct the IAmazonApiGatewayManagementApi which will be used to send the message to.
      var apiClient = ApiGatewayManagementApiClientFactory(endpoint);

      context.Logger.LogInformation($"Table scan of {ConnectionMappingTable} got {scanResponse.Items.Count} records.");
      
      // Loop through all of the connections and broadcast the message out to the connections.
      var count = 0;
      foreach (var item in scanResponse.Items)
      {
        var connectionId = item[ConnectionIdField].S;

        context.Logger.LogInformation($"Posting to connection {count}: {connectionId}");

        var postConnectionRequest = new PostToConnectionRequest
        {
          ConnectionId = connectionId, Data = stream
        };

        try
        {
          stream.Position = 0;
          await apiClient.PostToConnectionAsync(postConnectionRequest);
          context.Logger.LogInformation($"Posted to connection {count}: {connectionId}");
          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)
          {
            context.Logger.LogInformation($"Deleting gone connection: {connectionId}");
            var ddbDeleteRequest = new DeleteItemRequest
            {
              TableName = ConnectionMappingTable,
              Key = new Dictionary<string, AttributeValue>
              {
                {ConnectionIdField, new AttributeValue {S = connectionId}}
              }
            };

            await DDBClient.DeleteItemAsync(ddbDeleteRequest);
          }
          else
          {
            context.Logger.LogError(
              $"Error posting message to {connectionId}: {e.Message}");
            context.Logger.LogInformation(e.StackTrace);
          }
        }
        catch (Exception ex)
        {
          context.Logger.LogError($"Bugger, something fecked up: {ex.Message}");
          context.Logger.LogInformation(ex.StackTrace);
        }
      }

      return new APIGatewayProxyResponse
      {
        StatusCode = (int)HttpStatusCode.OK, Body = "Data sent to " + count + " connection" + (count == 1 ? "" : "s")
      };
    }
    catch (Exception e)
    {
      context.Logger.LogInformation("Error Sending Message: " + e.Message);
      context.Logger.LogInformation(e.StackTrace);
      return new APIGatewayProxyResponse
      {
        StatusCode = (int)HttpStatusCode.InternalServerError, Body = $"Failed to send message: {e.Message}"
      };
    }
2 Answers
0
Accepted Answer

Solved it by adding the credentials when creating the Api Client. Dont ask me why - it should be getting this from the role I thought:

    this.ApiGatewayManagementApiClientFactory = (Func<string, AmazonApiGatewayManagementApiClient>)((endpoint) =>
    {
      AWSCredentials awsCredentials = new EnvironmentVariablesAWSCredentials();
      return new AmazonApiGatewayManagementApiClient(awsCredentials, new AmazonApiGatewayManagementApiConfig
      {
        ServiceURL = endpoint
      });
    });
answered 2 years ago
0

Client should connect to wss://API ID.execute-api.REGION.amazonaws.com/STAGE.

When the client wants to send messages on the connection it should send requests to: https://API ID.execute-api.REGION.amazonaws.com/STAGE/@connections.

As far as I know, websockets are supported there, especially if your client is able to connect.

profile pictureAWS
EXPERT
Uri
answered 2 years ago
  • I'll try that again but pretty sure I did already. If I use a custom domain then I dont need /stage/ ??

  • Confirmed, that doesnt work either.

  • How does your sending code looks like?

  • Updated. Thanks for helping!!!

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions