koajs / koa

Expressive middleware for node.js using ES2017 async functions
https://koajs.com
MIT License
35.25k stars 3.23k forks source link

koa works strange with node-argon2 #670

Closed jim-y closed 8 years ago

jim-y commented 8 years ago

Hi!

Probably it's not the right place to report my problem, but I think it should be written down here too (also I will report it at node-argon2).

I tried to use argon2 in my koa application but I couldn't make it work because if I use node-argon2's hash method within a koa controller I get a strange timeout. I couldn't find out why this happens hence the question. I've created some dead simple examples to show the issue.

'use strict';
const koa = require('koa');
const app = module.exports = koa();
const argon2 = require('argon2');

function argon() {
    return argon2.generateSalt().then(salt => {
        console.log('SALT: ', salt);
        return argon2.hash('some@test.com', salt).then(hash => {
            console.log('HASH: ', hash);
            return argon2.verify(hash, 'some@test.com');
        });
    });
}

try {
    app.use(function *(){
        try {
            console.time('koa');
            yield argon();
            console.log('Hash verified');
            console.timeEnd('koa');
            this.body = 'Hash verified';
        }
        catch (err) {
            console.error(err);
        }
    });
    if (!module.parent) app.listen(3000);
}
catch (err) {
    console.error(err);
}

#########################
// curl -X GET localhost:3000
// Result:

SALT:  <Buffer d7 e6 f8 84 6c f6 e5 9d 32 61 16 37 1d 88 07 10>
HASH:  $argon2i$m=4096,t=3,p=1$1+b4hGz25Z0yYRY3HYgHEA$YSdD4Pw1EoLeGsfH5QrEB8OklnwlV/r2f

// curl -X GET localhost:3000
// Then I needed to call the service again to get these messages: 

Hash verified
koa: 322752.768ms

I tried to verify if this thing really only happens with koa so I modified the example two times

Example 1

Removed yield from the koa controller and used "plain" promises to create the hash, meaning I won't return a promise from the function call

'use strict';

const koa = require('koa');
const app = module.exports = koa();
const argon2 = require('argon2');

function argon() {
    argon2.generateSalt().then(salt => { 
        console.log('SALT: ', salt);
        argon2.hash('some@test.com', salt).then(hash => { 
            console.log('HASH: ', hash);
            argon2.verify(hash, 'some@test.com').then(() => {
                console.timeEnd('koa');
                console.log('Hash verified');
            });
        });
    });
}

try {

    app.use(function *(){
        try {
            console.time('koa');
            argon();
            this.body = 'Hash will be verified';
        }
        catch (err) {
            console.error(err);
        }
    });

    if (!module.parent) app.listen(3000);

}
catch (err) {
    console.error(err);
}

#########################
// curl -X GET localhost:3000
// Result:
Hash will be verified

// Then need to call it again to get
koa: 130070.741ms
Hash verified
SALT:  <Buffer 7b 23 99 9e 81 4f 5d 28 95 d8 fa d9 63 13 4e 69>
HASH:  $argon2i$m=4096,t=3,p=1$eyOZnoFPXSiV2PrZYxNOaQ$vFW1wXytkmaDe8lqsVAXmevYpVIH2SBngwfPjSfFbxc

It seems that koa fails to call an additional .next() on the iterator?!

Example 2

koa omitted

'use strict';
const argon2 = require('argon2');

function* compute() {
    try {
        yield crypto();
        console.log('Hash verified');
    }
    catch (err) {
        console.error(err);
    }
}

function crypto() {
    return argon2.generateSalt().then(salt => {
        console.log('SALT: ', salt);
        return argon2.hash('some@test.com', salt).then(hash => {
            console.log('HASH: ', hash);
            return argon2.verify(hash, 'some@test.com');
        });
    });
}

for (let _it of compute());

##############

$ node argon.js 
Hash verified
SALT:  <Buffer 0b 23 2c 4f 43 d0 f0 b4 70 86 98 9b b2 c5 9a 0d>
HASH:  $argon2i$m=4096,t=3,p=1$CyMsT0PQ8LRwhpibssWaDQ$ChdSIU7F1FQx1n7gvvoPxsXO+OVSCdqhTybh3ejV8uk

Any thoughts about this?

tags: koa, argon2, node, timeout, promise, yield

PlasmaPower commented 8 years ago

This is really odd. The same function works with co but not koa. The behavior when it doesn't work is also interesting: it will print the salt, when I ctrl+c curl it prints the hash, and then when I execute curl again it finishes and prints the salt again (continuing the loop). I've even tried requiring koa in my co example, but it still works.

With co (works): https://gist.github.com/PlasmaPower/fb3c12744d9bd35874b2

With koa (doesn't work): https://gist.github.com/PlasmaPower/e7c4e55c95f4bd7190d6

With koa and custom co wrapper (doesn't work): https://gist.github.com/PlasmaPower/fc4dea9bc7f3e5158fa2

With koa and custom co wrapper in separate call chain (doesn't work): https://gist.github.com/PlasmaPower/7a8c41efbdcc07a86fea

With koa and promises with experimental (doesn't work): https://gist.github.com/PlasmaPower/3ddd5f98f7e7cbfd2874

With koa@next and promises (doesn't work): https://gist.github.com/PlasmaPower/dfe0786ed601a4f1c3cc

PlasmaPower commented 8 years ago

I figured it out - argon doesn't work correctly when called from a require('http') server, which is what practically every server uses. Here's a broken example without koa: https://gist.github.com/PlasmaPower/f3cc2c06875eee760d81

I'd say close this issue, and change the title of the issue at argon2.

juliangruber commented 8 years ago

looks like some crazy side effect. since it was shown this doesn't have anything to do with koa itself, closing.

ranisalt commented 8 years ago

Just for documentation, it was fixed a while ago, and the issue were how v8 promises work internally.