Tiny Web服务器代码分析(2)

  先了解一下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>"); sprintf(body, "%s<body bgcolor=""ffffff"">\r\n",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); /*Print the HTTP response*/ sprintf(buf, "HTTP/1.0 %s %s\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参数

  parse_uri()将URI解析为一个文件名和一个可选的CGI参数字符串。如果是静态内容,我们将清除CGI参数串(即将cgiargs置空),然后将URI转换为一个相对的UNIX路径名,例如./home.html。如果URI是以/结尾的则要在后面添加默认文件名home.html。如果是动态内容,URI中的文件名和CGI参数是以?分割,以此可以抽出CGI参数和文件名。

int parse_uri(char *uri, char *filename, char *cgiargs) { char *ptr; if(!strstr(uri, "cgi-bin"))//static content { strcpy(cgiargs, ""); strcpy(filename, "."); strcat(filename, uri); if(uri[strlen(uri)-1] == '/') strcat(uri, "home.html"); return 1; } else { ptr = index(uri, '?'); if(ptr) { strcpy(cgiargs, ptr+1); *ptr = '\0'; } else strcpy(cgiargs, ""); strcpy(filename, "."); strcat(filename, uri); return 0; } } 6.serve_static()处理静态内容

  Tiny提供四种不同的静态内容:HTML文件、无格式文本文件、编码为GIF和JPEG格式的图片。
  serve_static()函数发送一个HTTP响应,响应的主体包括一个本地文件的内容。首先,我们检查文件名的后缀来判断文件类型,并在响应报头的Content-type字段中显示出来。随后我们发送响应行和响应报头给客户端,用一个空行来终止报头。
  然后我们以只读的方式打开所请求的文件获得文件句柄srcfd,并用mmap函数将文件映射到虚拟存储器空间,代码中是将srcfd指向的文件的前filesize个字节映射到一个地址从srcp开始的只读私有虚拟存储器区域。一旦映射成功我们就可以通过srcp来操作文件不再需要文件句柄了,所以我们关闭srcfd。然后我们使用Rio_writen()将文件传送到客户端。这时静态内容的处理操作已经完成了,我们释放srcp的虚拟存储器映射。

void serve_static(int fd, char *filename, int filesize) { int srcfd; char *srcp, filetype[MAXLINE], buf[MAXBUF]; /*Send response headers to client*/ get_filetype(filename,filetype); sprintf(buf, "HTTP/1.0 200 OK\r\n"); sprintf(buf, "%sServer: Tiny Web Server\r\n", buf); sprintf(buf, "%sContent-lenght: %d\r\n", buf, filesize); sprintf(buf, "%sContent-type: %s\r\n\r\n", buf, filetype); Rio_writen(fd, buf, strlen(buf)); /*Send response body to client*/ srcfd = Open(filename, O_RDONLY, 0); srcp = Mmap(0, filesize, PROT_READ, MAP_PRIVATE,srcfd,0); close(srcfd); Rio_writen(fd, srcp, filesize); Munmap(srcp, filesize); } void get_filetype(char *filename, char *filetype) { if(strstr(filename, ".html")) strcpy(filetype, "text/html"); else if(strstr(filename, ".gif")) strcpy(filetype, "image/gif"); else if(strstr(filename, ".jpg")) strcpy(filetype, "image/jpeg"); else strcpy(filetype, "text/plain"); } 7.serve_dynamic()处理动态内容

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

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