#include "common.h" void process_trans(int fd,int hit); void read_requesthdrs(rio_t *rp,int hit,int nrh); int is_static(char *uri); void parse_static_uri(char *uri, char *filename); void parse_dynamic_uri(char *uri, char *filename, char *cgiargs); void feed_static(int fd, char *filename, int filesize); void get_filetype(char *filename, char *filetype); void feed_dynamic(int fd, char *filename, char *cgiargs); void error_request(int fd, char *cause, char *errnum, char *shortmsg, char *description); /* 支持的文件扩展名及其对应的MIME类型 */ struct { char *ext; // 文件扩展名 char *filetype; // MIME类型 } extensions[] = { {".gif", "image/gif"}, {".jpg", "image/jpg"}, {".jpeg", "image/jpeg"}, {".png", "image/png"}, {".ico", "image/ico"}, {".zip", "image/zip"}, {".gz", "image/gz"}, {".tar", "image/tar"}, {".htm", "text/html"}, {".html", "text/html"}, {0, 0} // 结束标志 }; /* 调试模式宏定义 */ #ifdef DEBUG #define printf2(format, var) printf(format,var) #define printf3(format,var1,var2) printf(format,var1,var2) #else #define printf2(format, var) #define printf3(format,var1,var2) #endif /* 全局变量说明 */ void sigchld_handler(int sig) { while (waitpid(-1, 0, WNOHANG) > 0); return; } typedef void (*sighandler_t) (int sig) ; void Signal(int sig, sighandler_t handler){ struct sigaction sa; sa.sa_handler = sigchld_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; // 确保被信号中断的系统调用自动重启 sigaction(SIGCHLD, &sa, NULL); } /* Ctrl+C信号处理函数 */ void ctrlc_handler(int sig) { printf("您按下了Ctrl+C终止了Web服务器\n"); exit(0); } /* 主函数:监听端口并处理客户端连接 */ int main(int argc, char **argv) { int listen_sock, conn_sock, port; int hit; // 请求计数器 socklen_t clientlen; struct sockaddr_in clientaddr; /* 检查命令行参数 */ if (argc != 2) { fprintf(stderr, "用法: %s <端口号>\n", argv[0]); exit(1); } port = atoi(argv[1]); listen_sock = open_listen_sock(port); for (hit=1; ; hit++) { //hit为第几次请求,或第几次http事务 clientlen = sizeof(clientaddr); conn_sock = accept(listen_sock, (SA *)&clientaddr, &clientlen); if (fork() == 0) { close(listen_sock); /* Child process closes its listening socket */ process_trans(conn_sock,hit); // 处理HTTP事务 close(conn_sock); /* Child process closes connection with client */ exit(0); /* Child process exits */ } close(conn_sock); } exit(0); } /* 处理HTTP事务的核心函数 */ void process_trans(int fd,int hit) { int static_flag; // 是否为静态资源标志 struct stat sbuf; // 文件状态信息 char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE]; char filename[MAXLINE], cgiargs[MAXLINE]; rio_t rio; int rhn=0; // 请求头计数器 /* 设置Ctrl+C信号处理 */ if(signal(SIGINT,ctrlc_handler)==SIG_ERR) (void)printf("错误: 无法设置SIGINT信号处理\n"); printf2("第%d次请求开始\n",hit); /* 读取请求行和请求头 */ rio_readinitb(&rio, fd); rio_readlineb(&rio, buf, MAXLINE); printf3("请求头%d:%s", ++rhn, buf); sscanf(buf, "%s %s %s", method, uri, version); if (strcasecmp(method, "GET")) { // 仅支持GET方法 error_request(fd, method, "501", "未实现", "本服务器不支持该请求方法"); return; } read_requesthdrs(&rio,hit,rhn); // 读取剩余请求头 static_flag = is_static(uri); // 判断资源类型 if(static_flag) parse_static_uri(uri, filename); // 解析静态资源路径 else parse_dynamic_uri(uri, filename, cgiargs); // 解析动态资源路径和参数 /* 检查文件是否存在及权限 */ if (stat(filename, &sbuf) < 0) { error_request(fd, filename, "404", "未找到", "服务器无法找到该文件"); return; } /* 根据资源类型发送响应 */ if (static_flag) { // 静态资源处理 if (!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)) { error_request(fd, filename, "403", "禁止访问", "服务器无权读取该文件"); return; } feed_static(fd, filename, sbuf.st_size); // 发送静态文件内容 } else { // 动态资源处理 if (!(S_ISREG(sbuf.st_mode)) || !(S_IXUSR & sbuf.st_mode)) { error_request(fd, filename, "403", "禁止访问", "服务器无法执行该CGI程序"); return; } feed_dynamic(fd, filename, cgiargs); // 执行CGI程序并发送输出 } } /* 判断URI是否对应静态资源 */ int is_static(char *uri) { return strstr(uri, "cgi-bin") == NULL; // 不含cgi-bin则为静态资源 } /* 构造HTTP错误响应 */ void error_request(int fd, char *cause, char *errnum, char *shortmsg, char *description) { char buf[MAXLINE], body[MAXBUF]; /* 构建错误页面内容 */ sprintf(body, "
%s: %s
\r\n", body, description, cause); sprintf(body, "%s