Question

Webhook subscription is triggering with no body

  • 14 January 2023
  • 17 replies
  • 1377 views

Hi, We are recieving no body in webhook trigger from a subcription to telephony presence
/restapi/v1.0/account/~/extension/~/presence?detailedTelephonyState=true.
Note that this was working fine till yesterday and we haven't touched anything.

Are there any recent changes that might have caused this? How should I verify that my webhook url is recieving all the data in body that I need.


17 replies

Userlevel 1

I don't know what causes this, but I don't think that it is an actual telephony event trigger. I face the same issue with other event types and this is how I check and ignore such empty body (in Node JS code)

...
var body = []
req.on('data', function(chunk) {
  body.push(chunk);
}).on('end', function() {
  body = Buffer.concat(body).toString();
  if (!body || body == ""){
     console.log("Raw body: ", body)
     return
  }
  ...

I encountered the same problem starting a few days ago. It turned out that the webhooks are now being sent with Transfer-Encoding: chunked, which means there is no content-length header. We were looking for content-length > 0 to see if there was a message body to read, otherwise assuming it was the initial validation request. I just had to change it to always read the content body -- the web framework will handle the transfer encoding, and we'll get back an empty string if there is no body.

@Phong Vu @Andrew Gaskill By the time I got your response, issue was resolved without me doing anything, webhook started sending out body as well.

But now issue has resurfaced again. I am getting multiple triggers from webhook for single call and the request has no body. I also tried to read the chunked body as suggested above, but content is also empty from request.
How can I resolve my issue, is there a standard documentation for handling Transfer-Encoding:"chunked" request from ringcentrall?

Hi @IT Team , it will depend on your web framework. We use ASP.Net Core, so we just read the content body as

using var reader = new StreamReader(req.Body, Encoding.UTF8);
var content = await reader.ReadToEndAsync();

and then check to see if the content is empty or can be parsed as JSON. The transfer encoding is handled automatically.

Likewise if you are using node.js with express, you would use the `express.json()` middleware to transparently handle reading the content body as JSON data, and again the transfer-encoding is handled automatically.


Userlevel 1

And one more note to this. As you know that your Webhook URL is publicly accessible, meaning that if anyone knows the URL address, they can post whatever data to the address. To enhance and prevent such a situation where you may get fake/invalid events from unknown sources, use the verification-token to verify the integrity of the source of a post.

{
  eventFilters: [ '/restapi/v1.0/account/~/telephony/sessions' ],
  deliveryMode: {
    transportType: 'WebHook',
    address: 'https://f5c8-69-181-XXX-YY.ngrok.io/webhookcallback',
    verificationToken: 'ThisIsMySecrettoken'
  },
  expiresIn: 3600000
}
REQ HEADERS:  {
  host: 'f5c8-69-181-XXX-YY.ngrok.io',
  'user-agent': 'RingCentral-WebHook/8.3',
  'content-length': '0',
  accept: 'application/json',
  'accept-encoding': 'UTF-8',
  'content-type': 'application/json; charset=UTF-8',
  'validation-token': 'dd64fe80-cbab-4529-94ce-d52e087250dc',
  'verification-token': 'ThisIsMySecrettoken',
  'x-forwarded-for': '199.255.122.132',
  'x-forwarded-proto': 'https'
}

I think that I am having the same issue as @IT Team


I had a webhook setup in Azure that has been running fine since 2021, accepting event notifications from RingCentral using the /telephony/sessions filter. As of yesterday, it appears that the requests are coming through with no bodies or I don't know how to access the bodies properly in their new form. The webhook is an Azure function app written in Node.js. I don't actually know javascript, I just knocked up the original webhook from code samples and a bunch of trial and error, so I don't really have the know how to take @Andrew Gaskill 's solution and potentially make it work for my situation. Does anybody have any suggestions for how I should alter my code to be able to access the data that contains the actual telephony sessions?


module.exports = async function (context, req) {
    context.log('JavaScript HTTP trigger function processed a request.');

    if (req.query.name) {
        context.res = {
            // status: 200, /* Defaults to 200 */
            body: "Hello " + (req.query.name)
        };
        context.bindings.outputQueueItem = req.body;
        context.log('200');
        context.log(req);
        context.done();
    }
    if (req.body  && req.body.uuid ) {
        context.res = {
            // status: 200, /* Defaults to 200 */
            body: "Hello " + (req.uuid )
        };
        context.bindings.outputQueueItem = req.body;
        context.log('200');
        context.log(req);
        context.done();
    }
    if ( req.url && req.headers['validation-token'] ) {
        context.res = {
            // status: 200, /* Defaults to 200 */
            headers: {"Validation-Token": req.headers['validation-token']},
            body: "Hello " + (req.url)
           
        };
        context.bindings.outputQueueItem = req.body;
        context.log('200');
        context.log(req);
        context.done();
    }
    if ( req.url && req.headers['verification-token'] ) {
        context.res = {
            // status: 200, /* Defaults to 200 */
            headers: {"Validation-Token": req.headers['verification-token']},
            body: "Hello " + (req.url)
           
        };
        context.bindings.outputQueueItem = req.body;
        context.log('200');
        context.log(req);
        context.done();
    }
    if ( req.url ) {
        context.res = {
            // status: 200, /* Defaults to 200 */
            body: "Hello " 
           
        };
        context.bindings.outputQueueItem = req.body;
        context.log('200');
        context.log(req);
        context.done();
    }
    else {
        context.res = {
            status: 400,
            body: "Please pass a name on the query string or in the request body"
        };
        context.log('400');
        context.log(req);
        context.done();
    }
};

@prospect-health Looks to be a longstanding issue with Azure Functions nodejs runtime:

https://github.com/Azure/azure-functions-nodejs-worker/issues/524

Another thread linked from that thread suggests that adding a configuration property to hosts.json might fix it, but that might only be for specific functions runtime versions:

https://github.com/Azure/azure-functions-host/issues/4926#issuecomment-888600059


Thanks for looking into that for me. I think that looks like a rabbit hole too far for an amateur like me to dig into. I'm going to try setting up my subscriptions with PubNub via the RingCentral SDK and hopefully that will be robust enough for my needs.

Seems like we cannot get access to the chunked request when using php/apache because of a bug with apache. Does anyone know of a workaround that we can use without changing the configuration of our server? I've been beating my head against the wall for two days now!

Our current process is when we get a request from RingCentral without a body, we get faxes from the RC API, iterate through the response, check for ones that we haven't downloaded, etc.

https://www.jeffgeerling.com/blog/2017/apache-fastcgi-proxyfcgi-and-empty-post-bodies-chunked-transfer

Switching to Nginx seems to fix things for us which I looked into after reading this:

https://www.jeffgeerling.com/blog/2017/apache-fastcgi-proxyfcgi-and-empty-post-bodies-chunked-transfer

Running PHP on Apache, we sometimes would get a request with the content-length header -- but not the transfer-encoding header -- and we could easily get the contents of the request

{
  "content-length": "1081"
  "user-agent": "RingCentral-WebHook",
  "accept-encoding": "UTF-8",
  "accept": "application/json",
  "content-type": "application/json; charset=UTF-8",
  "verification-token": "xxx",
  "connection": "close
}


And sometimes get a request with the transfer-encoding header -- but not the content-length header -- and we were unable to get the contents of the request

{
  "transfer-encoding": "chunked",
  "user-agent": "RingCentral-WebHook",
  "accept-encoding": "UTF-8",
  "accept": "application/json",
  "content-type": "application/json; charset=UTF-8",
  "verification-token": "xxx",
  "connection": "close"
}


When testing on Nginx, the headers now include both the transfer-encoding and content-length header and we are able to get the body of the request as we did before (shrugs)

{
  "transfer-encoding": "chunked",
  "user-agent": "RingCentral-WebHook",
  "accept-encoding": "UTF-8",
  "accept": "application/json",
  "content-type": "application/json; charset=UTF-8",
  "verification-token": "xxx",
  "connection": "close",
  "content-length": "1081"
}
Userlevel 1

Supporting both transfer-encoding: chunked and content-length headers is correct and required. This is because the header is switched dynamically depending on the size of the content.

For some users, the issue might be caused by apache server. So no matter how you change your code, it simply doesn't work. You may need to update your apache server or change to nginx.

Ref: https://bz.apache.org/bugzilla/show_bug.cgi?id=63366

It looks like they rolled back the update, our Logic App is working again now.

Userlevel 1

Hi Developers,

I just want to let you know that we just rolled back the previous HTTP library which supports the content-length header. This roll-back is necessary and will be just a temporary solution for a certain period of time (about 30 days) so that developers would have enough time to prepare and update their web server that supports the transfer-encoding: chunked header.

For those who already successfully updated to newer web server that supports the transfer-encoding: chunked header should keep using that new web server since it should support both headers.

We will send out an official announcement with better information related to the change and timeline.

We apologize for any inconvenience this may have caused you due to this unintentional break.

@Phong Vu Is there any planned support for developers that use Azure Logic Apps to monitor RingCentral Webhooks? Azure support has reviewed the chunked header issue with us and said, "that is more of a limitation that won't be fixed anytime soon. There were internal discussions happening about this issue, but I think the fix is proving harder to do than expected. For now we just ask as a work around that the content length be set in the header when using chunking." Will this workaround be possible to implement?

Userlevel 1

Hi Iain et all,

I just had a discussion with the engineering team and the decision is that we will change the config to keep using the content-length header. So we will not roll back to use transfer-encoding: chunked anymore.

However, there is one remaining thing for us to decide is to add the body with some default content to the validation token post, the very first post that we send a validation-token header and the client app has to echo back the validation-token in the response header, in order to successfully create the webhook subscription. The reason we would add some content to the body is to keep the content-length header and set it to the length of the body content instead of content-length: 0.

I will announce when the final decision is made but you can assure to keep your code as it is right now.

I don't think there is any problem sending a Content-Length: 0 header if the body is empty. It had not been a problem prior to the change to chunked encoding. It is probably even okay to not send a Content-Length header at all if there is no content body. It was only a problem when there was no Content-Length header and there _was_ content to read.

Reply