question

IT Team avatar image
IT Team asked Phong Vu commented

Webhook subscription is triggering with no body

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.

webhooks
11 comments
1 |3000

Up to 8 attachments (including images) can be used with a maximum of 1.0 MiB each and 10.0 MiB total.

Andrew Gaskill avatar image Andrew Gaskill commented ·

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.

2 Likes 2 ·
IT Team avatar image IT Team commented ·

@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?

0 Likes 0 ·
Phong Vu avatar image Phong Vu ♦♦ IT Team commented ·

What programming language do you write your code?

0 Likes 0 ·
IT Team avatar image IT Team Phong Vu ♦♦ commented ·

Wea are using Microsoft Azure Logic App to listen to the HTTP Request.
Previously our data used to receive following headers and body as follows.

{
    "headers": {
        "Connection": "Keep-Alive",
        "Accept": "application/json",
        "Accept-Encoding": "UTF-8",
        "Host": "prod-05.centralus.logic.azure.com:443",
        "User-Agent": "RingCentral-WebHook/8.3",
        "Content-Length": "932",
        "Content-Type": "application/json; charset=UTF-8"
    },
    "body":{
                 ///Expected Body
         }
}



But now we are getting following header only without any body.

{
    "headers": {
        "Connection": "keep-alive",
        "Transfer-Encoding": "chunked",
        "Accept": "application/json",
        "Accept-Encoding": "UTF-8",
        "Host": "prod-13.centralus.logic.azure.com:443",
        "User-Agent": "RingCentral-WebHook",
        "Verification-Token": "hello",
        "Content-Type": "application/json; charset=UTF-8",
        "Content-Length": "0"
    }
}


@Phong Vu I am also using ASP.Net Core to simulate the current scenario. Can you provide the a more detailed code sample that also validates Verification Token.

0 Likes 0 ·
Show more comments
evandillon avatar image evandillon commented ·

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

0 Likes 0 ·
Phong Vu avatar image Phong Vu ♦♦ evandillon commented ·

This is how I read the webhook notification event payload in my PHP code. Check to see if you can apply the same in your project.

if (isset($_REQUEST['webhookcallback'])){
    if (array_key_exists('HTTP_VALIDATION_TOKEN', $_SERVER)) {
        return header("Validation-Token: {$_SERVER['HTTP_VALIDATION_TOKEN']}");
    }else{
      $jsonBody = file_get_contents('php://input');
      $body = json_decode($jsonBody, TRUE);
      print_r($body->uuid);
    }
}
0 Likes 0 ·
evandillon avatar image evandillon Phong Vu ♦♦ commented ·

@Phong Vu Thanks. But that does not work. What web server are you using?

// php code
Log::debug('RC CONTENT AS PHP://INPUT: ' . file_get_contents('php://input');

// log output
DEBUG: RC CONTENT AS PHP://INPUT:   
0 Likes 0 ·
Show more comments
Phong Vu avatar image
Phong Vu answered

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
  }
  ...
1 |3000

Up to 8 attachments (including images) can be used with a maximum of 1.0 MiB each and 10.0 MiB total.

Andrew Gaskill avatar image
Andrew Gaskill answered

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.


1 |3000

Up to 8 attachments (including images) can be used with a maximum of 1.0 MiB each and 10.0 MiB total.

Phong Vu avatar image
Phong Vu answered

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'
}
1 |3000

Up to 8 attachments (including images) can be used with a maximum of 1.0 MiB each and 10.0 MiB total.

prospect-health avatar image
prospect-health answered

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();
    }
};
1 |3000

Up to 8 attachments (including images) can be used with a maximum of 1.0 MiB each and 10.0 MiB total.

Andrew Gaskill avatar image
Andrew Gaskill answered prospect-health commented

@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


1 comment
1 |3000

Up to 8 attachments (including images) can be used with a maximum of 1.0 MiB each and 10.0 MiB total.

prospect-health avatar image prospect-health commented ·

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.

0 Likes 0 ·
evandillon avatar image
evandillon answered Andrew Gaskill commented

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"
}
3 comments
1 |3000

Up to 8 attachments (including images) can be used with a maximum of 1.0 MiB each and 10.0 MiB total.

Phong Vu avatar image Phong Vu ♦♦ commented ·

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.

0 Likes 0 ·
evandillon avatar image evandillon Phong Vu ♦♦ commented ·

@Phong Vu We get BOTH headers from RingCentral-WebHook user agent (The last example - no switching)

0 Likes 0 ·
Andrew Gaskill avatar image Andrew Gaskill evandillon commented ·

I believe what's happening there is nginx is reading the entire content body before sending it to your application, so it is adding content-length and presenting a standard content body, but then also leaving the transfer-encoding header from the original request, although it really shouldn't. Whereas Apache is passing the request through unmodified.

1 Like 1 ·
Tyler Liu avatar image
Tyler Liu answered

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

1 |3000

Up to 8 attachments (including images) can be used with a maximum of 1.0 MiB each and 10.0 MiB total.

Brian Niewohner avatar image
Brian Niewohner answered

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

1 |3000

Up to 8 attachments (including images) can be used with a maximum of 1.0 MiB each and 10.0 MiB total.

Phong Vu avatar image
Phong Vu answered Phong Vu commented

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.

2 comments
1 |3000

Up to 8 attachments (including images) can be used with a maximum of 1.0 MiB each and 10.0 MiB total.

@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?

0 Likes 0 ·
Phong Vu avatar image Phong Vu ♦♦ Brian Niewohner commented ·

@Brian Niewohner We are aware of this claim and we are also discussing about it internally to see how we can help dealing with such a limitation.

0 Likes 0 ·

Developer sandbox tools

Using the RingCentral Phone for Desktop, you can dial or receive test calls, send and receive test SMS or Fax messages in your sandbox environment.

Download RingCentral Phone for Desktop:

Tip: switch to the "sandbox mode" before logging in the app:

  • On MacOS: press "fn + command + f2" keys
  • On Windows: press "Ctrl + F2" keys