opentoken-io / opentoken

Public and free software API to tokenize or encrypt data.
MIT License
13 stars 5 forks source link

[ERR_ASSERTION]: async formatter for application/json requires callback to res.send() #138

Closed Neekoy closed 6 years ago

Neekoy commented 6 years ago

Hello,

I tried running the code locally, but unfortunately I'm getting the following error:

Unhandled rejection AssertionError [ERR_ASSERTION]: async formatter for application/json requires callback to res.send() (func) is required
    at foundFormatter (/home/neekoy/projects/opentoken/node_modules/restify/lib/response.js:449:20)
    at ServerResponse._findFormatter (/home/neekoy/projects/opentoken/node_modules/restify/lib/response.js:155:12)
    at ServerResponse.__send (/home/neekoy/projects/opentoken/node_modules/restify/lib/response.js:426:10)
    at ServerResponse.send (/home/neekoy/projects/opentoken/node_modules/restify/lib/response.js:278:17)
    at /home/neekoy/projects/opentoken/node_modules/restify/lib/server.js:978:29

I added some debug info to that function, and it would seem that most of the information is missing when the request is made:

File: /home/neekoy/projects/opentoken/node_modules/restify/lib/response.js

300 /**
301  * internal implementation of send. convenience method that handles:
302  * writeHead(), write(), end().
303  * @private
304  * @private
305  * @param    {Number} [maybeCode] http status code
306  * @param    {Object | Buffer | Error} [maybeBody] the content to send
307  * @param    {Object} [maybeHeaders] any add'l headers to set
308  * @param    {Function} [maybeCallback] optional callback for async formatters
309  * @param    {Function} format when false, skip formatting
310  * @returns  {Object} returns the response object
311  */
312 Response.prototype.__send =
313 function __send(maybeCode, maybeBody, maybeHeaders, maybeCallback, format) {
314 
315     var self = this;
316     var isHead = (self.req.method === 'HEAD');
317     var log = self.log;
318     var code;
319     var body;
320     var headers;
321     var callback = {};
322 
323     console.log("Maybe code: " + maybeCode);
324     console.log("Maybe body: " + maybeBody);
325     console.log("Maybe headers: " + maybeHeaders);
326     console.log("Maybe callback: " + maybeCallback);
327     console.log("Maybe format: " + format);

The above console logs return the following:

Maybe code: AssertionError [ERR_ASSERTION]: async formatter for application/json requires callback to res.send() (func) is required
Maybe body: undefined
Maybe headers: undefined
Maybe callback: undefined
Maybe format: true

Some system info:

$ cat /etc/*release*
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=18.04

$ node -v
v8.11.2

I tried several things, but I can't seem to get it going reliably. Can you please advise onto how I can fix this?

Cheers.

fidian commented 6 years ago

I'm about to go on a long camping trip and don't have time to investigate until after I return. Off the top of my head, I would probably suggest changing all of the formatters to use res.send() instead of done() and poke around in the restify formatters to see how they changed.

Neekoy commented 6 years ago

So I've been trying to do what you suggested but got stuck a bit. The formatter we're erroring for is specifically for "application/json" so I edited the following file: lib/formatter/json-formatter.js

"use strict";

/**
 * @param {opentoken~genericFormatter} genericFormatter
 * @return {Function}
 */
module.exports = (genericFormatter) => {
    return genericFormatter.formatWithFallback((req, res, body, done) => {
        var content;

        if (Buffer.isBuffer(body)) {
            content = body.toString("base64");
        } else if (body) {
            content = JSON.stringify(body);
        } else {
            content = "null";
        }

        // This is how it worked by default, changing it to use res.send instead of done
        // done(null, new Buffer(`${content}\n`, "binary"));

        let responseCode = res.statusCode;
        let responseBody = new Buffer(`${content}\n`, "binary");
        let responseHeaders = res.req.headers;
        let responseFormat = res.req.headers['content-type'];

        // Send function needs the following parameters
        // Response.prototype.__send =
        // function __send(maybeCode, maybeBody, maybeHeaders, maybeCallback, format)

        res.send(responseCode, responseBody, responseHeaders, null, responseFormat);

    });
};

I can't seem to figure out what callback function I should feed the send function so it would go through. Any help would be appreciated.

fidian commented 6 years ago

Restify was updated to version 5. This project was aimed at version 4. I'm pretty sure at the bottom of the 4TO5GUIDE.md in Restify, it covers this situation. However, I don't know if this really is a problem because the code only calls res.send() in a synchronous way and the formatters are set up properly from what I can tell.

I have replicated the issue you kindly reported. Thanks for providing so many details! The master branch has been updated and works with the example scripts. I would love to know if this also works for you. Updated code has been pushed to the master branch.

fidian commented 6 years ago

Turns out it is easier to use sync formatters instead of async send calls, so I'm changing the code to do that. I'm also fixing tests. Should be ready soon. Look for a commit that mentions this issue.

fidian commented 6 years ago

I'm having a huge amount of problems with the functional tests, which is something I had initially when writing them. In essence, node's internal HttpResponse object's .get method calls .getHeaders. That's implemented/overridden by Restify and it returns (this._headers || {}), which shouldn't be a problem. However, this._headers is defined as a getter that calls node's .get method, which calls .getHeaders. I need to break this loop somewhere, but I don't really know where the spot should be. :-/

You could just assume that it works fine. I could rewrite the functional tests to actually start the server and perform real tests, but then I wouldn't get the coverage stats for those conditions. Not sure what is my next step here, but at least you should have a working product.

Neekoy commented 6 years ago

Brilliant - thank you a whole lot. I gave it a couple of tests and it seems that it's working properly now with the upgrade to restify 5.

This can be marked as resolved I believe, thank you again :)