Tiny Web服务器代码分析

《深入理解计算机系统》中开发了一个小但是功能齐全的称为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)并且我们有读权限。如果是我们就向客户端提供静态内容。相似的如果请求的是动态内容,需要验证该文件是可执行文件,如果是我们就向客户端提供动态内容。

void doit(int fd) { int is_static; struct stat sbuf; char buf[MAXLINE], method[MAXLINE],uri[MAXLINE],version[MAXLINE]; char filename[MAXLINE],cgiargs[MAXLINE]; rio_t rio; /*read request line and headers*/ Rio_readinitb(&rio, fd); Rio_readlineb(&rio, buf, MAXLINE); sscanf(buf, "%s %s %s", method, uri, version); if(strcasecmp(method,"GET")) { clienterror(fd, method, "501","Not Implemented", "Tiny does not implement this method"); return; } read_requesthdrs(&rio); /*prase URI from GET request*/ is_static = parse_uri(uri, filename, cgiargs); if(stat(filename, &sbuf) < 0) { clienterror(fd, filename, "404","Not Found", "Tiny couldn't find this file"); return; } if(is_static)//server static content { if(!(S_ISREG(sbuf.st_mode) || !(S_IRUSR & sbuf.st_mode))) { clienterror(fd, filename, "403","Forbidden", "Tiny couldn't read the file"); return; } serve_static(fd, filename, sbuf.st_size); } else//server dynamic content { if(!(S_ISREG(sbuf.st_mode) || !(S_IXUSR & sbuf.st_mode))) { clienterror(fd, filename, "403","Forbidden", "Tiny couldn't run the CGI program"); return; } serve_dynamic(fd, filename, cgiargs); } } 3.clienterror()用于检查一些错误

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/590db02cf626d91f307c850cce3d03a9.html