《深入理解计算机系统》中开发了一个小但是功能齐全的称为Tiny的web服务器,这里是Tiny服务器的源码解析。
1.Tiny的main程序
Tiny是一个迭代服务器,通过命令行中传递来的端口值,调用Open_listenfd()函数打开一个监听套接字,然后Tiny执行无限循环:服务器阻塞在accept,等待监听描述符listenfd上的连接请求,当服务器从accept返回connfd,表明已经与客户端建立起了连接,执行事务,并关闭连接它的那一端,进行下一次循环。
#include "csapp.h" void doit(int fd); void read_requesthdrs(rio_t *rp); int parse_uri(char *uri, char *filename, char *cgiargs); void serve_static(int fd, char *filename, int filesize); void get_filetype(char *filename, char *filetype); void serve_dynamic(int fd, char *filename, char *cgiargs); void clienterror(int fd, char *cause, char *errnum, char *shorting,char *longmsg); int main(int argc,char *argv[]) { int listenfd,connfd,port,clientlen; struct sockaddr_in clientaddr; if(argc != 2) { fprintf(stderr,"usage: %s <port>\n",argv[0]); exit(0); } port = atoi(argv[1]); listenfd = Open_listenfd(port); while(1) { clientlen = sizeof(clientaddr); connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen); doit(connfd); Close(connfd); } } 2.doit()处理HTTP事务 先了解一下HTTP请求的组成。一个HTTP请求是由一个请求行,后面跟随零个或者更多个请求报头,再跟随一个空的文本行来终止报头列表。
HTTP请求行的格式如下。
<method><uri><version>
HTTP支持许多的方法,包括GET、POST、OPTIONS、HEAD、PUT、DELETE和TRACE。目前Tiny只支持GET方法,GET方法指导服务器生成和返回URI标识的内容。URI是URL的后缀,包括文件名和参数。版本字段表明了该请求遵循的HTTP版本,有HTTP/1.0和HTTP/1.1。
doit()处理HTTP事务。
读取并解析请求行,代码中使用Rio_readlineb()从fd读取一行数据到buf,然后分别写入变量method,uri和version。
Tiny不使用请求报头中的任何信息,使用read_requesthdrs()函数忽略掉报头的信息。
从请求中提取URI信息,使用parse_uri()来从URI中提取文件名和请求参数,并返回值标识静态内容或者动态内容。使用stat()获取文件的状态并将状态保存到sbuf中,执行成功返回0,如果执行失败则会返回-1表示该文件在磁盘上不存在。
如果请求的是静态内容,需要验证该文件是一般文件(st_mode == S_ISREG)并且我们有读权限。如果是我们就向客户端提供静态内容。相似的如果请求的是动态内容,需要验证该文件是可执行文件,如果是我们就向客户端提供动态内容。