boa是小巧、高效的web服务器,非常适合嵌入式系统。
命令行参数
-c server_root,用来改变working directory,默认是/etc/boa,
默认配置文件是boa.conf,所以修改working directory,会影响默认配置文件的读取。
-d 不fork
-f 指定配置文件的文件名。
-r chroot和chdir到那个目录,chroot后,所有的绝对路径都相对于设置的路径。
main入口
解析参数,设置server root,读取配置文件,打开log,创建server套接字,初始化信号处理函数,fork,进入loop循环。
log
有两种log,在参数里面对应:ErrorLog/AccessLog。前者会打开,并将标准错误绑定到它,如果ErrorLog为空,则保持标准错误不动。AccessLog会打开,并讲标准输出绑定到它,如果AccessLog为空,则使用/dev/null。
还有一个CGILog,如果有,则打开。
一个请求,有两个请求队列,blocked和ready。在loop中,如果request的某个状态的文件描述符是可读/写的,那么其从blocked队列放入ready队列。如果request完成了数据的响应,则将其放入blocked队列,直到loop再次将其放回ready队列进行处理。
loop有两种select和poll,以select为例:在select返回,执行fdset_update(),这这个里面,根据request的status,找到对应的fd,看其是否fdset,如果是,将其移到ready队列。注意:这里一个状态只对应一个fd,因此对于cgi这种中继的数据方式,会在PIPE_READ状态和PIPE_WRITE状态来回切。
status有下面这些
READ_HEADER
ONE_CR
ONE_LF
TWO_CR
BODY_READ
BODY_WRITE
WRITE
PIPE_READ
PIPE_WRITE
IOSHUFFLE
DONE
TIMEOUT
DEAD
WRITE/PIPE_WRITE
将响应写入fd(请求套接字)
BODY_WRITE
将POST请求的数据,写入post_data_fd(一个临时文件)
PIPE_READ
从data_fd(pipe,接受cgi的响应),读取数据,将来通过PIPE_WRITE写给client。
get_request(),accept新请求,然后分配一个新的请求,将fd设置到request->fd。将status初始化为READ
_HEADER。
process_request()
首先如果status小于TIMED_OUT,且有数据要写给client,则调用req_flush写数据,返回-1表示blocked,返回N,则表示写入了一些,还剩下多少。
接着,根据各个状态执行对应的处理函数:
READ_HEADER ONE_CR ONE_LF TWO_CR | read_header() |
BODY_READ | read_body() |
BODY_WRITE | write_body() |
WRITE | process_get() |
PIPE_READ | read_from_pipe() |
PIPE_WRITE | write_from_pipe() |
一个while循环,一个字符一个字符的解析,一个\r,则进入ONE_CR,接着一个\n,则进入ONE_LF,在跟着一个\r,则进入TWO_CR,在跟着一个\n,则进入BODY_READ。
如果状态是ONE_LF,则处理一个头,调用process_option_line()或process_logline(),后者是处理http的第一行,
如果状态是BODY_READ,则调用process_header_end()。
process_header_end()它解析请求的路径,根据document_root,确定对应的文件的路径,调用translate_uri,解析alias,如果是script,则解析对应的脚本的路径。如果是POST(只有cgi才允许POST),则创建临时文件,文件描述符post_data_fd并返回,如果是GET请求的cgi,则调用cgi,如果是GET请求静态页面,则status变为write,调用init_get,打开静态文件。
从套接字读取数据到缓存,读到一些数据后,将状态设置为BODY_WRITE。
将read_body读取的数据,写入临时文件(post_data_fd)。如果写完了,就调用了init_cgi,启动cgi进程。
init_cgi()
创建pipe,对于boa,读端fd存储在data_fd,对于fork出来的cgi进程,写端fd,绑定到标准输出。如果是POST请求,post_data_fd绑定到标准输入。
从data_fd,读取cgi的输出,
将从pipe读到的数据写入客户端套接字。
还有个IOSHUFFLE的状态,它处理partial request。