日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

tinyhttp源码阅读(注释)

發布時間:2023/12/18 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 tinyhttp源码阅读(注释) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這里就不細述了,代碼很簡單.
其實現的功能比較若,可以做一個參考.
因為其通過文件的權限位來判斷是否是一個CGI腳本,所以在權限位不對的情況下會判斷不正確.例如我將這個目錄放置在NTFS分區,所有的文件都有可執行權限,會導致將index.html文件當做CGI腳本.

注釋后的文件在這里下載http://files.cnblogs.com/files/oloroso/tinyhttpd-0.1.0.tar.7z

/* J. David's webserver */ /* This is a simple webserver.* Created November 1999 by J. David Blackstone.* CSE 4344 (Network concepts), Prof. Zeigler* University of Texas at Arlington*/ /* This program compiles for Sparc Solaris 2.6.* To compile for Linux:* 1) Comment out the #include <pthread.h> line.* 2) Comment out the line that defines the variable newthread.* 3) Comment out the two lines that run pthread_create().* 4) Uncomment the line that runs accept_request().* 5) Remove -lsocket from the Makefile.*/ #include <stdio.h> #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <ctype.h> #include <strings.h> #include <string.h> #include <sys/stat.h> #include <pthread.h> #include <sys/wait.h> #include <stdlib.h>#define ISspace(x) isspace((int)(x))#define SERVER_STRING "Server: jdbhttpd/0.1.0\r\n"// 接受請求,并處理 void accept_request(int clientsockfd); // 無法處理請求,回寫400到client void bad_request(int); // 將文件內容發送到client void cat(int client, FILE* fp); // 不可以執行CGI程序,回寫500到client void cannot_execute(int); // 輸出錯誤信息 void error_die(const char *); // 執行cgi程序 void execute_cgi(int, const char *, const char *, const char *); // 從fd中讀取一行,并將讀取的'\r\n'和'\r'轉換為'\n' int get_line(int fd, char* buf, int bufsize); // 返回200 OK給客戶端 void headers(int clientsockfd, const char * filename); // 返回404狀態給客戶端 void not_found(int); // 處理客戶端文件請求,發送404或200+文件內容到client void serve_file(int client, const char* filename); // 開啟soket監聽 int startup(u_short *); // 返回501狀態給客戶端 void unimplemented(int);/**********************************************************************/ /* A request has caused a call to accept() on the server port to* return. Process the request appropriately.* Parameters: the socket connected to the client */ /**********************************************************************/ void accept_request(int client) {char buf[1024];int numchars;char method[255]; // 方法(請求類型)char url[255]; // 請求的資源URLchar path[512]; // 請求資源的本地路徑size_t i, j;struct stat st;int cgi = 0; /* becomes true if server decides this is a CGI program *//* 為真時,表示服務器需調用一個CGI程序 */char *query_string = NULL;// 1. 從客戶端連接請求數據包中讀取一行numchars = get_line(client, buf, sizeof(buf));// 2. 從讀取的數據中提取出請求類型i = 0; j = 0;while (!ISspace(buf[j]) && (i < sizeof(method) - 1)){method[i] = buf[j];i++; j++;}method[i] = '\0';// 3. 判斷請求類型if (strcasecmp(method, "GET") && strcasecmp(method, "POST")){unimplemented(client); // 不支持請求類型,向客戶端返回501return;}if (strcasecmp(method, "POST") == 0){cgi = 1; // 如果是POST請求,調用cgi程序}// 4. 提取請求的資源URLi = 0;while (ISspace(buf[j]) && (j < sizeof(buf))){j++; // 定位下一個非空格位置}while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf))){url[i] = buf[j];i++; j++;}url[i] = '\0';if (strcasecmp(method, "GET") == 0){query_string = url; // 為GET請求,查詢語句為url// 如果查詢語句中含義'?',查詢語句為'?'字符后面部分while ((*query_string != '?') && (*query_string != '\0')){query_string++;}if (*query_string == '?'){cgi = 1;*query_string = '\0'; // 從'?'處截斷,前半截為urlquery_string++;}}// 5. 生成本地路徑sprintf(path, "htdocs%s", url);if (path[strlen(path) - 1] == '/'){strcat(path, "index.html"); //如果請求url是以'/'結尾,則指定為該目錄下的index.html文件}// 6. 判斷請求資源的狀態if (stat(path, &st) == -1) {// 從client中讀取,直到遇到兩個換行(起始行startline和首部header之間間隔)while ((numchars > 0) && strcmp("\n", buf)){ /* read & discard headers */numchars = get_line(client, buf, sizeof(buf));}not_found(client); // 501}else{// 請求的是個目錄,指定為該目錄下的index.html文件if ((st.st_mode & S_IFMT) == S_IFDIR){strcat(path, "/index.html");}// 請求的是一個可執行文件,作為CGI程序if ((st.st_mode & S_IXUSR) ||(st.st_mode & S_IXGRP) ||(st.st_mode & S_IXOTH) ){cgi = 1;}// 判斷是執行一個CGI程序還是返回一個文件內容給客戶端if (!cgi){serve_file(client, path); // }else{execute_cgi(client, path, method, query_string);}}close(client); }/**********************************************************************/ /* Inform the client that a request it has made has a problem.* Parameters: client socket */ /**********************************************************************/ void bad_request(int client) {char buf[1024];// 向client回寫400狀態sprintf(buf, "HTTP/1.0 400 BAD REQUEST\r\n");send(client, buf, sizeof(buf), 0);sprintf(buf, "Content-type: text/html\r\n");send(client, buf, sizeof(buf), 0);sprintf(buf, "\r\n");send(client, buf, sizeof(buf), 0);sprintf(buf, "<P>Your browser sent a bad request, ");send(client, buf, sizeof(buf), 0);sprintf(buf, "such as a POST without a Content-Length.\r\n");send(client, buf, sizeof(buf), 0); }/**********************************************************************/ /* Put the entire contents of a file out on a socket. This function* is named after the UNIX "cat" command, because it might have been* easier just to do something like pipe, fork, and exec("cat").* Parameters: the client socket descriptor* FILE pointer for the file to cat */ /**********************************************************************/ void cat(int client, FILE *resource) {char buf[1024];// 從resource中讀取一行fgets(buf, sizeof(buf), resource);// 將文件中的內容發送到客戶端while (!feof(resource)){send(client, buf, strlen(buf), 0);fgets(buf, sizeof(buf), resource);} }/**********************************************************************/ /* Inform the client that a CGI script could not be executed.* Parameter: the client socket descriptor. */ /**********************************************************************/ void cannot_execute(int client) {char buf[1024];// 向客戶端回寫500狀態,不可以執行CGI程序sprintf(buf, "HTTP/1.0 500 Internal Server Error\r\n");send(client, buf, strlen(buf), 0);sprintf(buf, "Content-type: text/html\r\n");send(client, buf, strlen(buf), 0);sprintf(buf, "\r\n");send(client, buf, strlen(buf), 0);sprintf(buf, "<P>Error prohibited CGI execution.\r\n");send(client, buf, strlen(buf), 0); }/**********************************************************************/ /* Print out an error message with perror() (for system errors; based* on value of errno, which indicates system call errors) and exit the* program indicating an error. */ /**********************************************************************/ void error_die(const char *sc) {perror(sc); // 輸出錯誤信息exit(1); // 退出程序 }/********************************************************************//** @brief 執行一個CGI腳本.可能需要設置適當的環境變量.* @prama client 客戶端socket文件描述符* @prama path CGI 腳本路徑* @prama method 請求類型* @prama query_string 查詢語句**********************************************************************/ void execute_cgi(int client, const char *path,const char *method, const char *query_string) {char buf[1024];int cgi_output[2]; // CGI程序輸出管道int cgi_input[2]; // CGI程序輸入管道pid_t pid;int status;int i;char c;int numchars = 1;int content_length = -1;buf[0] = 'A'; buf[1] = '\0';if (strcasecmp(method, "GET") == 0){// 從client中讀取,直到遇到兩個換行while ((numchars > 0) && strcmp("\n", buf)){ /* read & discard headers */numchars = get_line(client, buf, sizeof(buf));}}else /* POST */{// 獲取請求主體的長度numchars = get_line(client, buf, sizeof(buf));while ((numchars > 0) && strcmp("\n", buf)){buf[15] = '\0';if (strcasecmp(buf, "Content-Length:") == 0){content_length = atoi(&(buf[16]));}numchars = get_line(client, buf, sizeof(buf));}if (content_length == -1) {bad_request(client);return;}}// 向client回寫200 OKsprintf(buf, "HTTP/1.0 200 OK\r\n");send(client, buf, strlen(buf), 0);// 創建兩個匿名管道if (pipe(cgi_output) < 0) {cannot_execute(client);return;}if (pipe(cgi_input) < 0) {cannot_execute(client);return;}// 創建子進程,去執行CGI程序if ( (pid = fork()) < 0 ) {cannot_execute(client);return;}if (pid == 0) /* child: CGI script */{char meth_env[255];char query_env[255];char length_env[255];dup2(cgi_output[1], 1); //復制cgi_output[1](讀端)到子進程的標準輸出dup2(cgi_input[0], 0); //復制cgi_input[0](寫端)到子進程的標準輸入close(cgi_output[0]); //關閉多余文件描述符close(cgi_input[1]);sprintf(meth_env, "REQUEST_METHOD=%s", method);putenv(meth_env); // 添加一個環境變量if (strcasecmp(method, "GET") == 0) {sprintf(query_env, "QUERY_STRING=%s", query_string);putenv(query_env);}else { /* POST */sprintf(length_env, "CONTENT_LENGTH=%d", content_length);putenv(length_env);}// 執行CGI程序execl(path, path, NULL);exit(0); // 子進程退出}else { /* parent */close(cgi_output[1]); // 關閉cgi_output讀端close(cgi_input[0]); // 關閉cgi_input寫端if (strcasecmp(method, "POST") == 0){// 請求類型為POST的時候,將POST數據包的主體entity-body部分// 通過cgi_input[1](寫端)寫入到CGI的標準輸入for (i = 0; i < content_length; i++) {recv(client, &c, 1, 0);write(cgi_input[1], &c, 1);}}// 讀取CGI的標準輸出,發送到客戶端while (read(cgi_output[0], &c, 1) > 0){send(client, &c, 1, 0);}// 關閉多余文件描述符close(cgi_output[0]);close(cgi_input[1]);// 等待子進程結束waitpid(pid, &status, 0);} }/**********************************************************************/ /* Get a line from a socket, whether the line ends in a newline,* carriage return, or a CRLF combination. Terminates the string read* with a null character. If no newline indicator is found before the* end of the buffer, the string is terminated with a null. If any of* the above three line terminators is read, the last character of the* string will be a linefeed and the string will be terminated with a* null character.* Parameters: the socket descriptor* the buffer to save the data in* the size of the buffer* Returns: the number of bytes stored (excluding null) */ /**********************************************************************/ int get_line(int sock, char *buf, int size) {int i = 0;char c = '\0';int n;while ((i < size - 1) && (c != '\n')){// 從sock中讀取一個字節n = recv(sock, &c, 1, 0);/* DEBUG printf("%02X\n", c); */if (n > 0){// 將 \r\n 或 \r 轉換為'\n'if (c == '\r'){// 讀到了'\r'就再預讀一個字節n = recv(sock, &c, 1, MSG_PEEK);/* DEBUG printf("%02X\n", c); */// 如果讀取到的是'\n',就讀取,否則c='\n'if ((n > 0) && (c == '\n'))recv(sock, &c, 1, 0);elsec = '\n';}// 讀取數據放入bufbuf[i] = c;i++;}else{c = '\n';}}buf[i] = '\0';// 返回寫入buf的字節數return(i); }/**********************************************************************/ /* Return the informational HTTP headers about a file. */ /* Parameters: the socket to print the headers on* the name of the file */ /**********************************************************************/ void headers(int client, const char *filename) {char buf[1024];(void)filename; /* could use filename to determine file type */// 向客戶端回寫200應答strcpy(buf, "HTTP/1.0 200 OK\r\n");send(client, buf, strlen(buf), 0);strcpy(buf, SERVER_STRING);send(client, buf, strlen(buf), 0);sprintf(buf, "Content-Type: text/html\r\n");send(client, buf, strlen(buf), 0);strcpy(buf, "\r\n");send(client, buf, strlen(buf), 0); }/**********************************************************************/ /* Give a client a 404 not found status message. */ /**********************************************************************/ void not_found(int client) {char buf[1024];// 向client回寫404狀態sprintf(buf, "HTTP/1.0 404 NOT FOUND\r\n");send(client, buf, strlen(buf), 0);sprintf(buf, SERVER_STRING);send(client, buf, strlen(buf), 0);sprintf(buf, "Content-Type: text/html\r\n");send(client, buf, strlen(buf), 0);sprintf(buf, "\r\n");send(client, buf, strlen(buf), 0);sprintf(buf, "<HTML><TITLE>Not Found</TITLE>\r\n");send(client, buf, strlen(buf), 0);sprintf(buf, "<BODY><P>The server could not fulfill\r\n");send(client, buf, strlen(buf), 0);sprintf(buf, "your request because the resource specified\r\n");send(client, buf, strlen(buf), 0);sprintf(buf, "is unavailable or nonexistent.\r\n");send(client, buf, strlen(buf), 0);sprintf(buf, "</BODY></HTML>\r\n");send(client, buf, strlen(buf), 0); }/**********************************************************************/ /* Send a regular file to the client. Use headers, and report* errors to client if they occur.* Parameters: a pointer to a file structure produced from the socket* file descriptor* the name of the file to serve */ /**********************************************************************/ void serve_file(int client, const char *filename) {FILE *resource = NULL;int numchars = 1;char buf[1024];buf[0] = 'A'; buf[1] = '\0';// 從client中讀取,直到遇到兩個換行(起始行start line和首部header的間隔)while ((numchars > 0) && strcmp("\n", buf)){ /* read & discard headers */numchars = get_line(client, buf, sizeof(buf));}// resource = fopen(filename, "r");if (resource == NULL)not_found(client); // 404錯誤else{headers(client, filename); // 向客戶端回寫200 OKcat(client, resource); // 將resource指向文件中的內容寫入client}fclose(resource); }/**********************************************************************/ /* This function starts the process of listening for web connections* on a specified port. If the port is 0, then dynamically allocate a* port and modify the original port variable to reflect the actual* port.* Parameters: pointer to variable containing the port to connect on* Returns: the socket */ /**********************************************************************/ int startup(u_short *port) {int httpd = 0;struct sockaddr_in name;// 創建一個sockethttpd = socket(PF_INET, SOCK_STREAM, 0);if (httpd == -1){error_die("socket");}// 填寫綁定地址memset(&name, 0, sizeof(name));name.sin_family = AF_INET;name.sin_port = htons(*port);name.sin_addr.s_addr = htonl(INADDR_ANY);// 綁定socket到指定地址if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0){error_die("bind");}// 如果采用隨機分配端口,獲取它if (*port == 0) /* if dynamically allocating a port */{int namelen = sizeof(name);if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1)error_die("getsockname");*port = ntohs(name.sin_port);}// 監聽,等待連接if (listen(httpd, 5) < 0)error_die("listen");// 返回正在監聽的文件描述符return(httpd); }/**********************************************************************/ /* Inform the client that the requested web method has not been* implemented.* Parameter: the client socket */ /**********************************************************************/ void unimplemented(int client) {char buf[1024];// 向client回寫501狀態sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r\n");send(client, buf, strlen(buf), 0);sprintf(buf, SERVER_STRING);send(client, buf, strlen(buf), 0);sprintf(buf, "Content-Type: text/html\r\n");send(client, buf, strlen(buf), 0);sprintf(buf, "\r\n");send(client, buf, strlen(buf), 0);sprintf(buf, "<HTML><HEAD><TITLE>Method Not Implemented\r\n");send(client, buf, strlen(buf), 0);sprintf(buf, "</TITLE></HEAD>\r\n");send(client, buf, strlen(buf), 0);sprintf(buf, "<BODY><P>HTTP request method not supported.\r\n");send(client, buf, strlen(buf), 0);sprintf(buf, "</BODY></HTML>\r\n");send(client, buf, strlen(buf), 0); }/**********************************************************************/int main(void) {int server_sock = -1;u_short port = 0; // 使用隨機端口int client_sock = -1;struct sockaddr_in client_name;int client_name_len = sizeof(client_name);pthread_t newthread;// 啟動服務,監聽,等待連接server_sock = startup(&port);printf("httpd running on port %d\n", port);while (1){// 接受請求client_sock = accept(server_sock,(struct sockaddr *)&client_name,&client_name_len);if (client_sock == -1)error_die("accept");// 創建一個線程來處理請求/* accept_request(client_sock); */if (pthread_create(&newthread , NULL, accept_request, client_sock) != 0)perror("pthread_create");}// 關閉close(server_sock);return(0); }

轉載于:https://www.cnblogs.com/oloroso/p/5459196.html

總結

以上是生活随笔為你收集整理的tinyhttp源码阅读(注释)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。