Wishful Coding

Didn't you ever wish your
computer understood you?

JS server benchmark: Node.js & Rhino

Background

I’m playing with the idea of writing a little web framework in JavaScript using jQuery on both the server and the browser. To run jQuery on the server, you need — except for JavaScript — some magic, such as env.js and jsdom.

Env.js is a complete browser environment that allows you to run almost all browser JS code(like jQuery) on Rhino, a JavaScript engine written in Java. It is very complete, but tied to Rhino.

Jsdom is a younger project implementing only the DOM in CommonJS, though it is written for Node.js, an event-based network server based en Googles V8 JavaScript engine.

While I know some Java, and env.js does all I need, I got the impression that Node.js offers more in terms of speed and is more suitable for a web framework.

Since jsdom does not do AJAX and events, I’m facing the choice between using Rhino, or porting env.js to Node. I started to investigate the possibilities, and here is what I found.

Running the benchmark

Searching for benchmarks comparing server side JavaScript, I found none. I did find the JS benchmark Google uses to test V8, which might of course be skewed towards V8. I tried them anyway.

Node.js

Installing Node was a breeze, but getting the V8 benchmark to run on a V8 based framework was not so easy.

For some unknown reason the V8 benchmark uses load() to load everything together, while Node uses require(), with a slightly different meaning, which made simply substituting them impossible.

Stride on #node.js came up with the solution, consisting of concatenating the files together with some shell magic:

cat base.js crypto.js deltablue.js earley-boyer.js raytrace.js regexp.js richards.js splay.js run.js > v8bench.js

Note that the order of the files was important, otherwise a simple “cat *.js” would have sufficed.

Now all that remained was commenting out the original imports and defining a print function:

print = require('sys').log

Now this is the result:

$ node v8bench.js
19 Aug 16:13:25 - Crypto: 2434
19 Aug 16:13:26 - DeltaBlue: 3150
19 Aug 16:13:28 - EarleyBoyer: 12752
19 Aug 16:13:29 - RayTrace: 6230
19 Aug 16:13:30 - RegExp: 2287
19 Aug 16:13:31 - Richards: 1547
19 Aug 16:13:33 - Splay: 8879
19 Aug 16:13:33 - ----
19 Aug 16:13:33 - Score (version 5): 4090

Note the last number(average score) and the length of the command(2 parts).

Rhino

Installing Rhino was equally easy(just “port install rhino nodejs”)

Then the trouble started… First I had to figure out how to run Rhino, because it doesn’t come with its own command, like Node does. Turns out it’s “java -jar /path/to/js.jar”

After that, I ran ‘run.js’ from the v8 benchmark, and it just worked! Without modification! … Until it gave java.lang.OutOfMemoryError. Some Googling reveals that unless you have a memory leak in your app, you can increase the memory limit, which I did.

Here are the results for Rhino:

$ java -Xmx265m -jar js.jar run.js
Richards: 20.4
DeltaBlue: 138
Crypto: 120
RayTrace: 253
EarleyBoyer: 248
RegExp: 59.1
Splay: 279
----
Score (version 5): 120

Again note the score and the length of the command(5 parts). The -Xmx265m is for raising the memory limit.

My verdict

Sure, Node’s score is about 34 times the score of Rhino, but I talked to the author of Claypool, and he used Rhino for several production sites, without performance problems. It’s a measurement without a scale, so I can’t judge if it will matter to me.

With the speed sorted out, there is just the general style and comfort of both systems. Node.js is a very young C++ project, while Rhino has been around since the Netscape ages and is written in Java.

While I’m attracted to the speed and freshness of Node, I know more Java than I know C++, and there are tons of Java libs available to Rhino. And then there is env.js, which needs to be ported to Node, if I decide to use that.

Besides the porting and library issue, there is also hosting. Because Node is so new, you’d have to rent your own VPS, while Rhino can run in any Java servlet container, and even Google App Engine with the help of Rhino-for-webapps

So, my verdict? Undecided: As always, it depends on what you want, and I’m not yet sure what I want.

[update]

FireFox gets only 394 in the same test, so Rhino is probably not that bad.

I would be very grateful if anyone could give me some information on the CommonJS load/require and print problem or ways to speed up Rhino.

Twisted pop3 example server

Remember last time when I showed you the Twisted SMTP server? This time it's a POP3 server. I think it is more common to write SMTP servers as a part of a project(to send notifications and stuff), but I needed a POP3 server, so I think there might be others who need a POP3 server as well.

The process is rather similar to SMTP, only you need to implement the IMailbox interface. This is usually done with some sort of file or db based solution, but I used a simple list in this case. You might want to have a look at the mailbox module for a serious implementation.

Below you'll find a bare bones POP3 server, which can be run with `twistd -ny pop3.tac` and below that a sample telnet session, by running `telnet localhost 1230` in another terminal.

Connected to localhost.
Escape character is '^]'.
+OK <20100818095058.3878.894125537.0@pepijn-de-voss-imac.local>
user guest
+OK USER accepted, send PASS
pass password
+OK Authentication succeeded
stat
+OK 20 1020
retr 1
+OK 51
From: me
To: you
Subject: A test mail

Hello world!
.
retr 40
-ERR Bad message number argument
quit
+OK 
Connection closed by foreign host.
"""
An example pop3 server
"""

from twisted.application import internet, service
from twisted.cred.portal import Portal, IRealm
from twisted.internet.protocol import ServerFactory
from twisted.mail import pop3
from twisted.mail.pop3 import IMailbox
from twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse
from zope.interface import implements
from itertools import repeat
from hashlib import md5
from StringIO import StringIO

class SimpleMailbox:
    implements(IMailbox)

    def __init__(self):
        message = """From: me
To: you
Subject: A test mail

Hello world!"""
        self.messages = [m for m in repeat(message, 20)]


    def listMessages(self, index=None):
        if index != None:
            return len(self.messages[index])
        else:
            return [len(m) for m in self.messages]

    def getMessage(self, index):
        return StringIO(self.messages[index])

    def getUidl(self, index):
        return md5(self.messages[index]).hexdigest()

    def deleteMessage(self, index):
        pass

    def undeleteMessages(self):
        pass

    def sync(self):
        pass


class SimpleRealm:
    implements(IRealm)

    def requestAvatar(self, avatarId, mind, *interfaces):
        if IMailbox in interfaces:
            return IMailbox, SimpleMailbox(), lambda: None
        else:
            raise NotImplementedError()

portal = Portal(SimpleRealm())

checker = InMemoryUsernamePasswordDatabaseDontUse()
checker.addUser("guest", "password")
portal.registerChecker(checker)

application = service.Application("example pop3 server")

f = ServerFactory()
f.protocol = pop3.POP3
f.protocol.portal = portal
internet.TCPServer(1230, f).setServiceParent(application)
Published on

Check multiple twisted.cred checkers for a valid login

This is a snippet of a credential checker I wrote to authenticate a user
first via my DB, and if that fails via Twitter. It helped me to speed up
login, after the initial Twitter access tokens are stored.

In theory, this snippet could be used to authenticate against any number
of checkers, providing different interfaces. Only if all of them fail,
it returns an error.

from twisted.cred.checkers import ICredentialsChecker
from twisted.cred import error, credentials
from zope.interface import implements
from collections import deque

class CascadingChecker:
    """
    Check multiple checkers untill one succeeds.
    Else raise UnauthorizedLogin.
    """

    implements(ICredentialsChecker)
    credentialInterfaces = set()
    
    def __init__(self):
        self.checkers = []
        self.checked = []
    
    def registerChecker(self, checker):
        self.checkers.append(checker)
        self.credentialInterfaces.update(checker.credentialInterfaces)
    
    def _requestAvatarId(self, err, queue, credentials):
        try:
            ch = queue.popleft()
        except IndexError:
            raise error.UnauthorizedLogin()
        
        d = ch.requestAvatarId(credentials)
        return d.addErrback(self._requestAvatarId, queue, credentials)
    
    requestAvatarId = lambda self, credentials: self._requestAvatarId(None, deque(self.checkers), credentials)
from twisted.trial import unittest
from checkers import CascadingChecker
from twisted.cred.credentials import UsernamePassword
from twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse
from twisted.cred.error import UnauthorizedLogin
from twisted.cred.credentials import IUsernameHashedPassword, IUsernamePassword

class CascadingTest(unittest.TestCase):
    
    def setUp(self):
        ch1 = InMemoryUsernamePasswordDatabaseDontUse()
        ch2 = InMemoryUsernamePasswordDatabaseDontUse()
        self.cach = CascadingChecker()
        
        ch1.addUser('foo', 'bar')
        ch1.addUser('boo', 'far')
        ch2.addUser('for', 'bao')
        
        self.cach.registerChecker(ch1)
        self.cach.registerChecker(ch2)
    
    def testInterfaces(self):
        self.assertEquals(self.cach.credentialInterfaces.difference((IUsernameHashedPassword, IUsernamePassword)), set())
    
    def testLoginFirstChecker(self):
        user = UsernamePassword('foo', 'bar')
        return self.cach.requestAvatarId(user)
    
    def testLoginSecondChecker(self):
        user = UsernamePassword('for', 'bao')
        return self.cach.requestAvatarId(user)
    
    def testLoginFail(self):
        user = UsernamePassword('steve', 'pswd')
        self.assertFailure(self.cach.requestAvatarId(user), UnauthorizedLogin)