Tiny Web服务器代码分析

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()用于检查一些错误

  先了解一下HTTP响应行的组成。HTTP响应和HTTP请求是相似的。一个HTTP响应的组成有:一个响应行,后面跟随零个或者更多的响应报头,,再跟随一个终止报头的空行,再跟随响应主体。响应行的格式是:

<version><status code><status message>

  版本字段描述了响应所遵循的HTTP版本。状态码是一个三位的正整数,指明对请求的处理。状态消息给出与错误代码等价的英文描述。   clienterror()发送一个HTTP响应报文个给客户端,在响应行中包含状态码和状态消息,响应主体包含一个HTML文件,来向用户解释错误。

void clienterror(int fd, char *cause, char *errnum,char *shortmsg, char *longmsg){char buf[MAXLINE], body[MAXBUF];/*Build the HTTP response body*/sprintf(body, “<html><title>Tiny Error</title>”);,body);sprintf(body, “%s%s: %s\r\n”,body,errnum,shortmsg);sprintf(body, “%s<p>%s: %s\r\n”, body, longmsg, cause);sprintf(body, “%s<hr><em>The Tiny Web Server</em><>\r\n”,body);\r\n”,errnum, shortmsg);Rio_writen(fd, buf, strlen(buf));sprintf(buf, “Content-type: text/html\r\n”);Rio_writen(fd, buf, strlen(buf));sprintf(buf, “Content-length: %d\r\n\r\n”,(int)strlen(body));Rio_writen(fd, buf, strlen(buf));Rio_writen(fd, body, strlen(body));}4.read_requesthdrs()来跳过请求报头的信息,直到遇见表示报头结束的空文本行。void read_requesthdrs(rio_t *rp){char buf[MAXLINE];Rio_readlineb(rp, buf, MAXLINE);while(strcmp(buf, “\r\n”)){Rio_readlineb(rp, buf, MAXLINE);printf(“%s”, buf);}return;}5.parse_uri()解析URI参数纵然走过那么多城市,对于未知的风景,还是好奇。

Tiny Web服务器代码分析

相关文章:

你感兴趣的文章:

标签云: