10

We are experiencing an error when doing a SDK postToConnection() call as a promise, full error details given below. Other calls in the same function with a different connection ID happen successfully. Expected 410 connection errors are happening correctly and in milliseconds and are being handled gracefully.

This error then takes anything between 40 seconds to well over a minute to return, which causes it to always cause 'endpoint request timeout' errors in the Web socket API as it has a 30 second maximum request timeout. Has anybody experienced this issue before and/or any solution implemented? Any ideas to fix the issue will be highly appreciated, thanks.

UnknownError: Network error communicating with endpoint at Object.extractError (/opt/nodejs/node_modules/aws-sdk/lib/protocol/json.js:51:27)

4 Answers 4

20

are you trying to use postToConnection inside the connect handler? The websocket connection is only created after the connect handler has returned statusCode 200. You should not use postToConnection inside connect handler.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for the above suggestion, it now works as expected!
how do you send a message to a client that has just connected then? :)
@patrick Looks like the client should send an initial request to a separate route in order to receive the first message.
6

To avoid a 410 headache whilst employing websockets on serverless, don't forget to catch your exceptions:

export const ApiGatewayConnector = (event) => {

  const endpoint = process.env.IS_OFFLINE
            ? 'http://localhost:3001'
            : `${event.requestContext.domainName}/${event.requestContext.stage}`
            const apiVersion = '2018-11-29'
            return new AWS.ApiGatewayManagementApi({ apiVersion, endpoint })
}

....

if (event.requestContext.stage == 'local') {
              await ApiGatewayConnector(event)
              .postToConnection({ ConnectionId, Data })
              .promise()
              .catch(_ => removeId(ConnectionId));<----- N.B. Remove disconnected IDs
            } else {
              await ws.send(Data, ConnectionId)
            }
}
        

Comments

1

That error is thrown when calling .postToConnection in answer to a $connect event. You can call .postConnection without errors in answer to a $default event.

// index.js
// the handler is defined as: index.handler

const AWS = require("aws-sdk");

exports.handler = function (event, context, callback) {

    console.log('event.requestContext.eventType', event && event.requestContext && event.requestContext.eventType)

    if (event.requestContext.eventType === "CONNECT") {

        console.log('$connect event')

        // calling apigwManagementApi.postToConnection will throw an exception

        callback(null, {
            statusCode: 200,
            body: "Connected"
        });

    } else if (event.requestContext.eventType === "DISCONNECT") {

        console.log('$disconnect event')

        // calling apigwManagementApi.postToConnection is pointless since the client has disconneted

        callback(null, {
            statusCode: 200,
            body: "Disconnected"
        });
    } else {

        console.log('$default event')

        const ConnectionId = event.requestContext.connectionId
        const bodyString = event.body
        const Data = bodyString

        const apigwManagementApi = new AWS.ApiGatewayManagementApi({
            apiVersion: "2018-11-29",
            endpoint: event.requestContext.domainName + "/" + event.requestContext.stage
        });

        apigwManagementApi
        .postToConnection({ ConnectionId, Data })
        .promise().then(() => {

            callback(null, {
                statusCode: 200,
                body: "Disconnected"
            });

        })

    }

};

Comments

0

Not really sure if that follows the best practice, but I was able to use postToConnection inside the connect handler, (by calling the callback before initiating postToConnection)

exports.handler = async (event, context, callback) => {
  try {
    const {
      body = '{}',
      requestContext: { apiId, connectionId, routeKey, stage },
    } = event;

    const apigwManagementApi = new ApiGatewayManagementApi({
      endpoint: 'local' === stage ? `http://localhost:4002` : `https://${apiId}.execute-api.${process.env.AWS_REGION}.amazonaws.com/${stage}`,
    });

    switch(routeKey) {
      case '$connect':

        // call callback to create connection
        callback(null, {
          statusCode: 200,
          body: "Connected"
        });

        await apigwManagementApi.postToConnection({
          ConnectionId: connectionId,
          Data: JSON.stringify({
            action: 'test-message'
          }),
        }).promise();

        break;
      case '$disconnect':
        console.log('DISCONNECT');
        break;
      default:
        console.log('DEFAULT');
        break;
    }

    return { statusCode: 200 };
  } catch (error) {
    console.log('Got error', error)
  }
};

1 Comment

I tried this but it simpl wouldn't wokr for me

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.