October 8th, 2007

Libevent has pure http API

It's now long time since my last post.

This surely does not means that I'm stopping FAPWS, at contrary I'm more and more convinced that one of the beauty of Python reside in his gluing capabilities.
This observation is probably not new to lot of people, but for my personal case it's better now than never ;-), no ?

After that small philosophical intro, please go back to our main interest: integrate libevent within the python world.
What's great with libevent is that since several release, they have introduced several HTTP low level APIs: send_reply, send_error, ...
I've tried to use them thanks to pyrex, ctype and swig.

With every of those tools I'm facing issues, problems, ... to get a webserver working correctly.
Because I want FAPWS to be light, efficient and stable, I don't like the idea to add layers on which I don't have too much control. Hoooooo, I'm already hearing some voices ;-). The point is not linked with those tools, but more to my competencies to be able to have them working correctly. Indeed, when you are new to C (which is my case) and try to understand how to use swig, trust me, this is not piece of cake.
Thus I'll use the simple, low level Python API for this project. This will be a way for me to learn C and C API of Python.


Will it be easy to learn C from Python ?

We will see.
Any how, any helps are welcome ;-).





Here after, you can see a minimalist webserver serving the famous "Hello World!":
import ctypes
import sys

libevent=ctypes.cdll.LoadLibrary('libevent.so')

def root_handler(req, *arg):
    #print "got request:", req
    libevent.evbuffer_new.restype=ctypes.c_void_p
    #Build a buffer and put page content into it
    buf=libevent.evbuffer_new()
    libevent.evbuffer_add_printf.argtypes=[ctypes.c_void_p, ctypes.c_char_p]
    libevent.evbuffer_add_printf(buf,"Hello world!\n\n")
    #send the buffer with a return code of 200
    libevent.evhttp_send_reply.argtypes=[ctypes.c_void_p, ctypes.c_int, ctypes.c_char_p, ctypes.c_void_p]
    libevent.evhttp_send_reply(req, 200, "OK", buf)
    return 1


    
def main():
    #We just initialise and give the server address and port
    libevent.event_init()
    libevent.evhttp_start.argtypes=[ctypes.c_char_p, ctypes.c_short]
    libevent.evhttp_start.restype=ctypes.c_void_p
    http=libevent.evhttp_start('0.0.0.0',8080)
    #we lik the call to root_handler at a request of "/"
    FUNC=ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p)
    libevent.evhttp_set_cb.argtypes=[ctypes.c_void_p, ctypes.c_char_p, ctypes.c_void_p]
    libevent.evhttp_set_cb(http,"/",FUNC(root_handler), None)
    #we loop and wait events ;-)
    libevent.event_dispatch()
    #we close the loop
    print "end loop main" 
    libevent.evhttp_free.argtypes=[ctypes.c_void_p]
    libevent.evhttp_free(http)

if __name__=="__main__":
    main()

Note: I don't know exsactly why, but the server does not stop when you hit "-c". You must kill it ;-(.

Amazingly, I'm able to get 1700#/sec on my 1.2Ghz Athlon.



Annexes:

[myhost ~]$ ab -n2000 -c10 http://127.0.0.1:8080/
This is ApacheBench, Version 2.0.40-dev <$Revision: 1.146 $> apache-2.0
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Copyright 2006 The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)

Server Software:
Server Hostname:        127.0.0.1
Server Port:            8080

Document Path:          /
Document Length:        14 bytes

Concurrency Level:      10
Time taken for tests:   1.170227 seconds
Complete requests:      2000
Failed requests:        0
Write errors:           0
Total transferred:      196000 bytes
HTML transferred:       28000 bytes
Requests per second:    1709.07 [#/sec] (mean)
Time per request:       5.851 [ms] (mean)
Time per request:       0.585 [ms] (mean, across all concurrent requests)
Transfer rate:          163.22 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.2      0       3
Processing:     2    5   2.5      4      15
Waiting:        1    5   2.6      4      15
Total:          2    5   2.5      4      15

Percentage of the requests served within a certain time (ms)
  50%      4
  66%      4
  75%      7
  80%      7
  90%      9
  95%     11
  98%     13
  99%     14
 100%     15 (longest request)
[myhost ~]$