📄 mini_ws.c
字号:
/* * web_server.c - A primary Web server . */#include <stdio.h>#include <errno.h>#include <sys/types.h>#include <sys/socket.h>#include <sys/wait.h>#include <sys/stat.h>#include <sys/mman.h>#include <unistd.h>#include <signal.h>#include <fcntl.h>#include <netinet/in.h>#define MAXLINE 150typedef struct sockaddr SA;extern char **environ; /* defined by libc */void handle_req(int fd); 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 exec_cgi(int fd, char *method, int content_length, char *filename, char *cgiargs);void error_msg(int fd, char *cause, char *errnum, char *shortmsg, char *longmsg);int main(int argc, char **argv) { /* Check command line args */ if (argc != 2) { fprintf(stderr, "usage: %s <port>\n", argv[0]); exit(1); } signal(SIGCHLD, SIG_IGN); signal(SIGPIPE, SIG_IGN); int port = atoi(argv[1]); int listen_fd; struct sockaddr_in client_addr; struct sockaddr_in server_addr; if((listen_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { perror("Unable to obtain network"); exit(1); } int optval = 1; if((setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&optval, sizeof(int))) < 0) { perror("setsockopt failed"); exit(1); } server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); server_addr.sin_addr.s_addr = htonl(INADDR_ANY); if(bind(listen_fd, (SA *)&server_addr, sizeof(server_addr)) < 0) { perror("Unable to bind socket"); exit(1); } if(listen(listen_fd, 1024) < 0) { perror("Unable to listen"); exit(1); } #ifdef DEBUG printf("listening... s=%d \n", listen_fd);#endif int conn_fd, len; while (1) { len = sizeof(client_addr); if((conn_fd = accept(listen_fd, (SA *)&client_addr, &len)) < 0) { exit(1); close(listen_fd); }#ifdef DEBUG printf("connected. fd=%d \n", conn_fd);#endif handle_req(conn_fd); close(conn_fd); }}/* * handle one HTTP request/response transaction */void handle_req(int fd) { char buf[MAXLINE], method[MAXLINE], uri[MAXLINE], version[MAXLINE]; char filename[MAXLINE], cgiargs[MAXLINE]; FILE *f = fdopen(fd, "r"); if (!f) { perror("Unable to open input fd"); close(fd); return; } setbuf(f, 0); /* Get HTTP command line*/ if (!fgets(buf, MAXLINE, f)) { perror("Error reading buffer"); fclose(f); return; } sscanf(buf, "%s %s %s", method, uri, version);#ifdef DEBUG printf("method: %s, uri: %s, version: %s \n", method, uri, version);#endif if (strcasecmp(method, "GET") && strcasecmp(method, "POST")) { error_msg(fd, method, "501", "Not Implemented", "MiniWebServer does not support this method yet!"); return; } /* read headers and parse "Content-length"*/ char buf_header[MAXLINE]; int content_length = 0; while (fgets(buf_header, 150, f) && (strlen(buf_header) > 2)) { if (strncasecmp(buf_header, "Content-length:", 15) == 0) { content_length = atoi(buf_header + 15); } }#ifdef DEBUG printf("content length: %d \n", content_length);#endif /* Parse URI */ int is_cgi = parse_uri(uri, filename, cgiargs); struct stat sbuf; if (stat(filename, &sbuf) < 0) { error_msg(fd, filename, "404", "Not found", "Couldn't find this file"); return; } if (is_cgi) { /* Serve CGI request */ if (!(S_ISREG(sbuf.st_mode)) || !(S_IXUSR & sbuf.st_mode)) { error_msg(fd, filename, "403", "Forbidden", "You have no permission to run the CGI program"); return; } exec_cgi(fd, method, content_length, filename, cgiargs); } else { /* Serve static content */ if (!(S_ISREG(sbuf.st_mode)) || !(S_IRUSR & sbuf.st_mode)) { error_msg(fd, filename, "403", "Forbidden", "You have no permission to read the file"); return; } serve_static(fd, filename, sbuf.st_size); }}/* * parse_uri - parse URI into filename and CGI args * return 1 if cgi request, 0 if static */int parse_uri(char *uri, char *filename, char *cgiargs) { char *ptr; if (!strstr(uri, "cgi-bin")) { /* Static content */ strcpy(cgiargs, ""); strcpy(filename, "."); /*serve the current directory*/ strcat(filename, uri); if (uri[strlen(uri)-1] == '/') strcat(filename, "index.html"); return 0; } else { /* CGI request */ ptr = index(uri, '?'); if (ptr) { strcpy(cgiargs, ptr+1); *ptr = '\0'; } else strcpy(cgiargs, ""); strcpy(filename, "."); strcat(filename, uri); return 1; }}/* * serve_static - copy a file back to the client */void serve_static(int fd, char *filename, int filesize) {#ifdef DEBUG printf("serve static file: %s \n", filename);#endif int srcfd; char *srcp, filetype[MAXLINE], buf[MAXLINE]; /* Send response headers to client */ get_filetype(filename, filetype); sprintf(buf, "HTTP/1.0 200 OK\r\n"); sprintf(buf, "%sServer: Mini 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); write(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); write(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");} /* * exec_cgi - run a CGI program on behalf of the client */void exec_cgi(int fd, char *method, int content_length, char *filename, char *cgiargs) { char buf[MAXLINE], *emptylist[] = { NULL }; /* Return first part of HTTP response */ sprintf(buf, "HTTP/1.1 200 OK\r\n"); write(fd, buf, strlen(buf)); sprintf(buf, "Server: Mini Web Server\r\n"); write(fd, buf, strlen(buf)); if (fork() == 0) { /* child */ /*handle POST method*/ if (strcasecmp(method, "POST") == 0) { int pfd[2]; int rc = pipe(pfd); if (rc < 0) { perror("pipe in POST failed"); exit(1); } int post_pid = fork(); if (post_pid == 0) { close(pfd[0]); int n = 0, bytes_read = 0; /*only read length of "content_length"*/ while (n < content_length) { bytes_read = read(fd, buf, sizeof(buf)-1); printf("content read: %s \n", buf); if (bytes_read > 0) { write(pfd[1], buf, bytes_read); n += bytes_read; } } exit(0); } close(pfd[1]); /*redirect to STDIN*/ dup2(pfd[0],STDIN_FILENO); } /* set CGI vars, only support "QUERY_STRING" and "CONTENT_LENGTH" */ setenv("QUERY_STRING", cgiargs, 1); sprintf(buf, "%d", content_length); setenv("CONTENT_LENGTH", buf); dup2(fd, STDOUT_FILENO); /* Redirect stdout to client */ execve(filename, emptylist, environ); /* Run CGI program */ } wait(NULL); /* Parent waits for and reaps child */}/* * error_msg - returns an error message to the client */void error_msg(int fd, char *cause, char *errnum, char *shortmsg, char *longmsg) { char buf[MAXLINE], body[MAXLINE]; /* Build the HTTP response body */ sprintf(body, "<html><title>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>Mini Web server</em>\r\n", body); /* Print the HTTP response */ sprintf(buf, "HTTP/1.0 %s %s\r\n", errnum, shortmsg); write(fd, buf, strlen(buf)); sprintf(buf, "Content-type: text/html\r\n"); write(fd, buf, strlen(buf)); sprintf(buf, "Content-length: %d\r\n\r\n", (int)strlen(body)); write(fd, buf, strlen(buf)); write(fd, body, strlen(body));}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -