diff --git a/tiny.c b/tiny.c new file mode 100644 index 0000000..0cc8b1a --- /dev/null +++ b/tiny.c @@ -0,0 +1,251 @@ +// #param warning(disable:4996) +#include "csapp.h" +#include + +int read_ini(char* filename,const char* key,void* value); +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 *shortmsg, char *longmsg); +char add_log[1024]; +int main(int argc, char **argv) +{ + int listenfd, connfd, port, clientlen; + struct sockaddr_in clientaddr; + // read_ini("webserver.ini","port", + + // if (argc != 2) { + // fprintf(stderr, "usage: %s \n", argv[0]); + // exit(1); + // } + // port = atoi(argv[1]); + char ini[1024]; + if (read_ini("webserver.ini","port",&ini)==-1){ + printf("read_ini error\n"); + return 0; + } + port=atoi((const char*)ini); + printf("\n\n\n%d\n\n\n",port); + + listenfd = Open_listenfd(port);// 打开一个监听套接字 + while (1) {// 执行典型的无限服务器循环,不断接受连接请求 + clientlen = sizeof(clientaddr); + connfd = Accept(listenfd, (SA *)&clientaddr, &clientlen); + char ipv4_address[1024]; + inet_ntop(AF_INET,&clientaddr.sin_addr,ipv4_address,INET_ADDRSTRLEN); + time_t nowt=time(0); + struct tm* nowlt=localtime(&nowt); + sprintf(add_log,"%d/%d/%d %d:%d:%d IP:%s ",\ + nowlt->tm_year+1900,nowlt->tm_mon,nowlt->tm_mday,nowlt->tm_hour,\ + nowlt->tm_min,nowlt->tm_sec,ipv4_address); + doit(connfd); // 执行事务 + Close(connfd); + } +} + +// 简单的读取配置 +int read_ini(char* filename,const char* key,void* value){ + FILE* fl; + if ((fl=fopen(filename,"r"))==NULL){ + perror("fopen"); + return -1; + } + char sLine[1024]; + while ((fgets(sLine,1024,fl))!=NULL){ + if (strncmp(sLine,"//",2)==0) continue; + if ('#'==sLine[0]) continue; + char* eq=strchr(sLine,'='); + if (NULL!=eq && strncmp(key,sLine,strlen(key))==0){ + sLine[strlen(sLine)-1]='\0'; + fclose(fl); + printf("\n\n\n%siswhat\n\n\n",(char*)eq); + while (*(eq+1)==' ' && *(eq+1)!='.') eq++; + strcpy(value,eq+1); + return 0; + } + } + fclose(fl); + return -1; +} + + +// doit 处理请求 +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; + + Rio_readinitb(&rio, fd); + Rio_readlineb(&rio, buf, MAXLINE); // 读取浏览器的请求 + sscanf(buf, "%s %s %s", method, uri, version); // 格式化读取请求 + if (strcasecmp(method, "GET")) { // 只能处理GET + clienterror(fd, method, "501", "Not Implemented", + "Tiny does not implement this method"); + return; + } + sprintf(add_log,"%s%s %s\n",add_log,method,uri); + FILE* fl; + if ((fl=fopen("webserver.log","a+"))!=NULL){ + fputs((const char*)add_log,fl); + add_log[0]='\0'; + fclose(fl); + } + read_requesthdrs(&rio); // 读并忽略请求报头 + + // Parse 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) { // Serve static content + if (!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)) { + // S_ISREG 此文件是否是普通文件 S_IRUSR & sbuf.st_mode 用户是否具有读权限 + clienterror(fd, filename, "403", "Forbidden", + "Tiny couldn't read the file"); + return; + } + serve_static(fd, filename, sbuf.st_size); //servestatic + } + else { // Serve dynamic content + if (!(S_ISREG(sbuf.st_mode)) || !(S_IXUSR & sbuf.st_mode)) { //executable + clienterror(fd, filename, "403", "Forbidden", + "Tiny couldn't run the CGI program"); + return; + } + serve_dynamic(fd, filename, cgiargs); //servedynamic + } +} + +// read_requesthdrs - read and parse HTTP request headers +void read_requesthdrs(rio_t *rp) +{ + char buf[MAXLINE]; + + Rio_readlineb(rp, buf, MAXLINE); + while(strcmp(buf, "\r\n")) { //line:netp:readhdrs:checkterm + Rio_readlineb(rp, buf, MAXLINE); + printf("%s", buf); + } + return; +} + +// parse_uri - parse URI into filename and CGI args +// return 0 if dynamic content, 1 if static +int parse_uri(char *uri, char *filename, char *cgiargs) +{ + char *ptr; + + if (!strstr(uri, "cgi-bin")) { // Static content + strcpy(cgiargs, ""); + if (read_ini("webserver.ini","root",filename)==-1){ + printf("read_ini error\n"); + return -1; + } + if (filename[0]!='/') strcpy(filename, "."); + printf("\n\n\n%sis root\n\n\n",filename); + // strcpy(filename, "."); + strcat(filename, uri); + if (uri[strlen(uri)-1] == '/') + strcat(filename, "index.html"); + return 1; + } + else { // Dynamic content + ptr = index(uri, '?'); + if (ptr) { + strcpy(cgiargs, ptr+1); + *ptr = '\0'; + } + else + strcpy(cgiargs, ""); + strcpy(filename, "."); + strcat(filename, uri); + return 0; + } +} + +// serve_static - copy a file back to the client +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-length: %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); +} + +// get_filetype - derive file type from file name +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"); +} + +// serve_dynamic - run a CGI program on behalf of the client +void serve_dynamic(int fd, char *filename, char *cgiargs) +{ + char buf[MAXLINE], *emptylist[] = { NULL }; + + // Return first part of HTTP response + sprintf(buf, "HTTP/1.0 200 OK\r\n"); + Rio_writen(fd, buf, strlen(buf)); + sprintf(buf, "Server: Tiny Web Server\r\n"); + Rio_writen(fd, buf, strlen(buf)); + + if (Fork() == 0) { + // Real server would set all CGI vars here + setenv("QUERY_STRING", cgiargs, 1); + Dup2(fd, STDOUT_FILENO); // Redirect stdout to client + Execve(filename, emptylist, environ); // Run CGI program + } + Wait(NULL); // Parent waits for and reaps child +} + +// clienterror - returns an error message to the client +void clienterror(int fd, char *cause, char *errnum, + char *shortmsg, char *longmsg) +{ + char buf[MAXLINE], body[MAXBUF]; + + // Build the HTTP response body + sprintf(body, "Tiny Error"); + sprintf(body, "%s\r\n", body); + sprintf(body, "%s%s: %s\r\n", body, errnum, shortmsg); + sprintf(body, "%s

%s: %s\r\n", body, longmsg, cause); + sprintf(body, "%s


The Tiny Web server\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)); +}