There don't seem to be many AJP C libraries. In fact, there don't seem to be any (according to Google, at least). There's at least one FastCGI C library, which is unsurprising given the ubiquity of FastCGI. So yesterday afternoon, I decided to "read spec, write code" yet again and began a C implementation of the "container" (app server) side of AJP. After not having touched C for over 2-3 years, it was a good feeling to muck around with C and BSD sockets again. (Procedural programming, how I missed you!)
I finished it up in a few hours and it is now fairly complete. It's actually a pretty simple protocol, I've realized. All the complexity comes from the way requests/responses are encoded and decoded. (Otherwise, it's a fairly straightforward 1:1 mapping.)
Of course there were the 3 undocumented spec additions, the first two I had to figure out through experimentation so long ago and the last was conveyed to me by someone who actually looked at the source mod_jk/mod_proxy_ajp source. (As much as I believe in the whole "the best documentation is the source" thing, I don't really like looking at similar/related source code when implementing something.)
Anyhow, I don't think those 3 undocumented additions are documented anywhere (hah!) besides my source (ajp_base.py and ajp.c). So:
- When decoding strings, a string with the length of 0xffff is the same as the empty string. However, its trailing NUL is not in the stream.
- When sending SEND_BODY_CHUNK packets, the packets must be NUL terminated. However, this NUL must not be included in the SEND_BODY_CHUNK's length (but must be included in the packet's length).
- The value following an SSL_KEY_SIZE attribute is not an encoded string, but rather an AJP integer.
Anyway, the ultimate goal of this project is to create a Python WSGI AJP server that is implemented in C as much as possible. At this point, I have a simple proof-of-concept working that makes calls into an embedded Python interpreter. It doesn't implement WSGI at all though.
As far as request/response throughput is concerned, it looks promising. While the threaded pure-Python AJP server could only handle ~86 requests per second (with 100 parallel clients), the threaded C/Python hybrid version was nearly pushing 1000 requests per second. Of course it doesn't include WSGI overhead yet. But we shall see.
All this effort is, of course, inspired by the WSGI servers built upon the FastCGI C library:
fcgiapp and
python-fastcgi. If I actually
used FastCGI, I'd probably be using one of those servers rather than flup's.