├── www ├── 1.txt ├── index.html ├── c.py ├── 手册.txt └── index.py ├── LightCgiServer ├── README.md ├── .gitignore ├── LICENSE ├── LightCgiServer.h ├── tags └── LightCgiServer.c /www/1.txt: -------------------------------------------------------------------------------- 1 | hello world 2 | -------------------------------------------------------------------------------- /LightCgiServer: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/imyouxia/LightCgiServer/HEAD/LightCgiServer -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | LightCgiServer 2 | ============== 3 | 4 | 使用Posix C 编写的轻量级CGI服务器。 5 | 6 | 编译: 7 | 8 | gcc -Wall -g LightCgiServer.c -o LightCgiServer 9 | 10 | 11 | -------------------------------------------------------------------------------- /www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Welcome! 5 | 6 | 7 |

Welcome!

8 |

If you are seeing this, it's because you've successfully built klange's Simple CGI Server!

9 | 10 | 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Libraries 8 | *.lib 9 | *.a 10 | 11 | # Shared objects (inc. Windows DLLs) 12 | *.dll 13 | *.so 14 | *.so.* 15 | *.dylib 16 | 17 | # Executables 18 | *.exe 19 | *.out 20 | *.app 21 | *.i*86 22 | *.x86_64 23 | *.hex 24 | -------------------------------------------------------------------------------- /www/c.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | print "Content-type:text/html\r\n\r\n" 4 | print '' 5 | print '' 6 | print 'Hello Word - First CGI Program' 7 | print '' 8 | print '' 9 | print '

Hello Word! This is my first CGI program

' 10 | print '' 11 | print '' 12 | 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 imyouxia 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /www/手册.txt: -------------------------------------------------------------------------------- 1 | 一、操作系统是Ubuntu下的操作步骤 2 | 1.net-snmp的安装 3 | (1)tar xzvf net-snmp-5.7.2.tar.gz 4 | (2)cd net-snmp-5.7.2 5 | (3)安装依赖环境 6 | sudo apt-get install libperl-dev 7 | (4)配置net-snmp 8 | ./configure –prefix=/usr/local/snmp –with-mib-modules=ucd-snmp/diskio 9 | (5)编译并安装 10 | make 11 | sudo make install 12 | (6)配置文件 snmpd.conf 13 | sudo cp EXAMPLE.conf /usr/local/snmp/share/snmp/snmpd.conf 14 | (7)sudo apt-get install snmpd 15 | (8)service snmp restart 16 | 17 | 2.libmysql的安装 18 | (1)安装mysql服务端:sudo apt-get install mysql-server 19 | (2)安装Mysql客户端:sudo apt-get install mysql-client 20 | (3)安装Mysql开发工具:sudo apt-get install libmysqlclient-dev 21 | (4)sudo ldconfig -v 22 | 23 | 3.cron定时任务 24 | vim /etc/crontab之后输入 25 | 0 1 * * * findtopo位置/findtopo 26 | 27 | 28 | 二、操作系统是Centos下的操作步骤 29 | 30 | 1.net-snmp的安装 31 | (1)tar xzvf net-snmp-5.7.2.tar.gz 32 | (2)cd net-snmp-5.7.2 33 | (3)配置net-snmp 34 | ./configure --prefix=/usr/local/snmp –with-mib-modules=ucd-snmp/diskio 35 | (4)编译并安装 36 | make 37 | make install 38 | (5)配置文件 snmpd.conf 39 | sudo cp EXAMPLE.conf /usr/local/snmp/share/snmp/snmpd.conf 40 | (6)service snmpd restart 41 | 42 | 2.libmysql的安装 43 | 64位系统: 44 | rpm -ivh mysql-connector-c-devel-6.1.1-1.linux_glibc2.5.x86_64.rpm 45 | rpm -ivh mysql-connector-c-shared-6.1.1-1.linux_glibc2.5.x86_64.rpm 46 | 32位系统: 47 | rpm -ivh mysql-connector-c-devel-6.1.1-1.linux_glibc2.5.i386.rpm 48 | rpm -ivh mysql-connector-c-shared-6.1.1-1.linux_glibc2.5.i386.rpm 49 | 50 | service mysql start 51 | 52 | 3.cron定时任务 53 | 1.安装crontab 54 | yum install vixie-cron 55 | yum install crontabs 56 | 57 | 2.设置cron开机启动 58 | service crond restart 59 | chkconfig –-level 35 crond on 60 | 61 | 0 1 * * * findtopo位置/findtopo 62 | 63 | 64 | -------------------------------------------------------------------------------- /www/index.py: -------------------------------------------------------------------------------- 1 | import string,cgi,time 2 | from os import curdir, sep 3 | from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer 4 | 5 | class MyHandler(BaseHTTPRequestHandler): 6 | 7 | def do_GET(self): 8 | try: 9 | if self.path.endswith(".html") or self.path.endswith(".txt"): 10 | f = open(curdir + sep + self.path) #self.path /index.html 11 | self.send_response(200) 12 | self.send_header('Content-type', 'text/html') 13 | self.end_headers() 14 | self.wfile.write(f.read()) 15 | f.close() 16 | return 17 | if self.path.endswith(".py"): 18 | f = open(curdir + sep + self.path) 19 | self.send_response(200) 20 | self.send_header('Content-type', 'text/html') 21 | self.end_headers() 22 | self.wfile.write(f.read()) 23 | f.close() 24 | return 25 | if self.path.endswith(".sth"): #dynamic 26 | self.send_response(200) 27 | self.send_header('Content-type', 'text/html') 28 | self.end_headers() 29 | self.wfile.write("today " + str(time.localtime()[7])) 30 | self.wfile.write("year " + str(time.localtime()[0])) 31 | return 32 | 33 | return 34 | 35 | except IOError: 36 | self.send_error(404,'File Not Found: %s' % self.path) 37 | 38 | 39 | def do_POST(self): 40 | global rootnode 41 | try: 42 | ctype, pdict = cgi.parse_header(self.headers.getheader('content-type')) 43 | if ctype == 'multipart/form-data': 44 | query=cgi.parse_multipart(self.rfile, pdict) 45 | self.send_response(301) 46 | 47 | self.end_headers() 48 | upfilecontent = query.get('upfile') 49 | print "filecontent", upfilecontent[0] 50 | self.wfile.write("POST OK.

"); 51 | self.wfile.write(upfilecontent[0]); 52 | 53 | except : 54 | pass 55 | 56 | def main(): 57 | try: 58 | server = HTTPServer(('', 8080), MyHandler) 59 | print 'started httpserver...' 60 | server.serve_forever() 61 | except KeyboardInterrupt: 62 | print '^C received, shutting down server' 63 | server.socket.close() 64 | 65 | if __name__ == '__main__': 66 | main() 67 | -------------------------------------------------------------------------------- /LightCgiServer.h: -------------------------------------------------------------------------------- 1 | #ifndef LightCgiServer_H_ 2 | #define LightCgiServer_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | struct socket_request 21 | { 22 | int fd; //socket 文件描述符 23 | struct sockaddr_in remote_addr; //远程连接地址 24 | pthread_t thread; //线程 25 | }; 26 | 27 | typedef struct _queue 28 | { 29 | void **buf; 30 | unsigned int size; 31 | unsigned int allSize; 32 | }queue_t; 33 | 34 | 35 | // Request Variables 36 | typedef struct _request_h 37 | { 38 | char* filename; //Filename was received(ie,/index.html) 39 | char* querystring; //Query String,URL Encoded 40 | int request_type; //Request type,GET = 0,POST = 1,HEAD = 2... 41 | char* _filename; //FileName relative to server 42 | char* ext; //Extension for requested file 43 | char* host; //Hostname for request 44 | char* http_version; //HTTP version used in request 45 | unsigned long c_length; //Content-Type,usually for POST 46 | char* c_type; //Content-Type,usually for POST 47 | char* c_cookie; //HTTP_COOKIE 48 | char* c_uagent; //User-Agent,for cgi 49 | char* c_referer; //Referer,for cgi 50 | }request_h; 51 | 52 | struct cgi_data 53 | { 54 | int fd; // 读描述符 55 | int fd2; // 写描述符 56 | int pid; // Process ID 57 | }; 58 | 59 | #define PORT 80 //定义服务器默认端口号 60 | #define HEADER_SIZE 10240//request请求头的最大字节 61 | #define QUEUE_SIZE 1024 //队列个数 62 | #define CGI_POST 10240 //动态脚本传送的数据 63 | #define HTML_SIZE 10240 //HTML文本最大字节 64 | #define PAGES "www" //HTML文件目录 65 | 66 | 67 | void handleSignal(int sigNo); 68 | void disconnect(struct socket_request* socket,FILE *fd,request_h* request_header); 69 | queue_t* alloc_queue(); 70 | void queue_append(queue_t* queue,void* value); 71 | unsigned int queue_size(queue_t* queue); 72 | void free_queue(queue_t* queue); 73 | void* queue_at(queue_t* queue,unsigned int id); 74 | void delete_queue(queue_t* queue); 75 | char from_hex(char c); 76 | void generic_response(FILE* fd,char* status,char* message); 77 | void* wait_pid(void* data); 78 | void unsupport(struct socket_request* socket,queue_t* queue,FILE* fd,request_h* request_header); 79 | int processHeader(struct socket_request* socket,queue_t* queue,FILE* fd,int* type_width,request_h* request_header); 80 | int request_file(struct socket_request* socket,queue_t* queue,FILE* fd,request_h* request_header); 81 | int set_env(struct socket_request* socket,queue_t* queue,request_h* request_header); 82 | int check_dir(FILE* fd,request_h* request_header,struct stat* Stats); 83 | int determine_mime(queue_t* queue,FILE* fd,FILE* content,request_h* request_header); 84 | void pipe_trans(queue_t* queue,FILE* fd,FILE* cgi_w,request_h* request_header); 85 | int read_header(struct socket_request* socket,queue_t* queue,FILE* fd,FILE* cgi_r,request_h* request_header,pthread_t* waitthread); 86 | int parse_html(struct socket_request* socket,queue_t* queue,FILE* fd,request_h* request_header); 87 | int readRequest(struct socket_request* socket,queue_t* queue,FILE* fd,request_h* request_header); 88 | void* handleRequest(void* socket); 89 | 90 | #endif 91 | 92 | -------------------------------------------------------------------------------- /tags: -------------------------------------------------------------------------------- 1 | !_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/ 2 | !_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/ 3 | !_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/ 4 | !_TAG_PROGRAM_NAME Exuberant Ctags // 5 | !_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/ 6 | !_TAG_PROGRAM_VERSION 5.9~svn20110310 // 7 | CGI_POST LightCgiServer.h 62;" d 8 | HEADER_SIZE LightCgiServer.h 60;" d 9 | HTML_SIZE LightCgiServer.h 63;" d 10 | LightCgiServer_H_ LightCgiServer.h 2;" d 11 | PAGES LightCgiServer.h 64;" d 12 | PORT LightCgiServer.h 59;" d 13 | QUEUE_SIZE LightCgiServer.h 61;" d 14 | _filename LightCgiServer.h /^ char* _filename; \/\/FileName relative to server$/;" m struct:_request_h 15 | _queue LightCgiServer.h /^typedef struct _queue$/;" s 16 | _request_h LightCgiServer.h /^typedef struct _request_h$/;" s 17 | allSize LightCgiServer.h /^ unsigned int allSize;$/;" m struct:_queue 18 | alloc_queue LightCgiServer.c /^queue_t* alloc_queue()$/;" f 19 | buf LightCgiServer.h /^ void **buf;$/;" m struct:_queue 20 | c_cookie LightCgiServer.h /^ char* c_cookie; \/\/HTTP_COOKIE$/;" m struct:_request_h 21 | c_length LightCgiServer.h /^ unsigned long c_length; \/\/Content-Type,usually for POST$/;" m struct:_request_h 22 | c_referer LightCgiServer.h /^ char* c_referer; \/\/Referer,for cgi$/;" m struct:_request_h 23 | c_type LightCgiServer.h /^ char* c_type; \/\/Content-Type,usually for POST$/;" m struct:_request_h 24 | c_uagent LightCgiServer.h /^ char* c_uagent; \/\/User-Agent,for cgi$/;" m struct:_request_h 25 | cgi_data LightCgiServer.h /^struct cgi_data$/;" s 26 | check_dir LightCgiServer.c /^int check_dir(FILE* fd,request_h* request_header,struct stat* Stats)$/;" f 27 | delete_queue LightCgiServer.c /^void delete_queue(queue_t* queue)$/;" f 28 | determine_mime LightCgiServer.c /^int determine_mime(queue_t* queue,FILE* fd,FILE* content,request_h* request_header)$/;" f 29 | disconnect LightCgiServer.c /^void disconnect(struct socket_request *socket,FILE *fd,request_h* request_header)$/;" f 30 | ext LightCgiServer.h /^ char* ext; \/\/Extension for requested file$/;" m struct:_request_h 31 | fd LightCgiServer.h /^ int fd; \/\/ 读描述符$/;" m struct:cgi_data 32 | fd LightCgiServer.h /^ int fd; \/\/socket 文件描述符$/;" m struct:socket_request 33 | fd2 LightCgiServer.h /^ int fd2; \/\/ 写描述符$/;" m struct:cgi_data 34 | filename LightCgiServer.h /^ char* filename; \/\/Filename was received(ie,\/index.html)$/;" m struct:_request_h 35 | free_queue LightCgiServer.c /^void free_queue(queue_t* queue)$/;" f 36 | from_hex LightCgiServer.c /^char from_hex(char c)$/;" f 37 | generic_response LightCgiServer.c /^void generic_response(FILE* fd,char* status,char* message)$/;" f 38 | handleRequest LightCgiServer.c /^void *handleRequest(void *socket)$/;" f 39 | handleSignal LightCgiServer.c /^void handleSignal(int sigNo)$/;" f 40 | host LightCgiServer.h /^ char* host; \/\/Hostname for request$/;" m struct:_request_h 41 | http_version LightCgiServer.h /^ char* http_version; \/\/HTTP version used in request$/;" m struct:_request_h 42 | main LightCgiServer.c /^int main(int argc,char *argv[])$/;" f 43 | parse_html LightCgiServer.c /^int parse_html(struct socket_request* socket,queue_t* queue,FILE* fd,request_h* request_header)$/;" f 44 | pid LightCgiServer.h /^ int pid; \/\/ Process ID$/;" m struct:cgi_data 45 | pipe_trans LightCgiServer.c /^void pipe_trans(queue_t* queue,FILE* fd,FILE* cgi_w,request_h* request_header)$/;" f 46 | port LightCgiServer.c /^int port; \/\/默认端口$/;" v 47 | processHeader LightCgiServer.c /^int processHeader(struct socket_request* socket,queue_t* queue,FILE* fd,int* type_width,request_h* request_header)$/;" f 48 | querystring LightCgiServer.h /^ char* querystring; \/\/Query String,URL Encoded$/;" m struct:_request_h 49 | queue_append LightCgiServer.c /^void queue_append(queue_t *queue,void *value)$/;" f 50 | queue_at LightCgiServer.c /^void *queue_at(queue_t* queue,unsigned int id)$/;" f 51 | queue_size LightCgiServer.c /^unsigned int queue_size(queue_t* queue)$/;" f 52 | queue_t LightCgiServer.h /^}queue_t;$/;" t typeref:struct:_queue 53 | readRequest LightCgiServer.c /^int readRequest(struct socket_request* socket,queue_t* queue,FILE *fd,request_h* request_header)$/;" f 54 | read_header LightCgiServer.c /^int read_header(struct socket_request* socket,queue_t* queue,FILE* fd,FILE* cgi_r,request_h* request_header,pthread_t* waitthread)$/;" f 55 | remote_addr LightCgiServer.h /^ struct sockaddr_in remote_addr; \/\/远程连接地址$/;" m struct:socket_request typeref:struct:socket_request::sockaddr_in 56 | request_file LightCgiServer.c /^int request_file(struct socket_request* socket,queue_t* queue,FILE* fd,request_h* request_header)$/;" f 57 | request_h LightCgiServer.h /^}request_h;$/;" t typeref:struct:_request_h 58 | request_type LightCgiServer.h /^ int request_type; \/\/Request type,GET = 0,POST = 1,HEAD = 2...$/;" m struct:_request_h 59 | set_env LightCgiServer.c /^int set_env(struct socket_request* socket,queue_t* queue,request_h* request_header)$/;" f 60 | size LightCgiServer.h /^ unsigned int size;$/;" m struct:_queue 61 | socket_request LightCgiServer.h /^struct socket_request$/;" s 62 | sockfd LightCgiServer.c /^int sockfd; \/\/socket$/;" v 63 | thread LightCgiServer.h /^ pthread_t thread; \/\/线程$/;" m struct:socket_request 64 | unsupport LightCgiServer.c /^void unsupport(struct socket_request* socket,queue_t* queue,FILE* fd,request_h* request_header)$/;" f 65 | wait_pid LightCgiServer.c /^void* wait_pid(void* data)$/;" f 66 | -------------------------------------------------------------------------------- /LightCgiServer.c: -------------------------------------------------------------------------------- 1 | #include "LightCgiServer.h" 2 | 3 | 4 | int port; //默认端口 5 | int sockfd; //socket 6 | 7 | // SIGINT信号处理函数 8 | void handleSignal(int sigNo) 9 | { 10 | printf("Shutting down the Server!\n"); 11 | //终止sock通信的读取和传送操作 12 | shutdown(sockfd,2); 13 | close(sockfd); 14 | exit(sigNo); 15 | } 16 | 17 | // 链接终止,释放内存 18 | void disconnect(struct socket_request *socket,FILE *fd,request_h* request_header) 19 | { 20 | struct socket_request *request = socket; 21 | if(fd) 22 | { 23 | fclose(fd); 24 | } 25 | 26 | // 终止读取和传送操作 27 | shutdown(request->fd,2); 28 | 29 | // 将子线程的状态设置为detached,则该线程运行结束后将自动释放所有资源 30 | if(request->thread) 31 | { 32 | pthread_detach(request->thread); 33 | } 34 | 35 | free(request_header); 36 | free(request); 37 | 38 | } 39 | 40 | // 将request header依次放到队列里,初始化队列 41 | queue_t* alloc_queue() 42 | { 43 | queue_t* queue = (queue_t*)malloc(sizeof(struct _queue)); 44 | queue->buf = (void **)malloc(QUEUE_SIZE * sizeof(void *)); 45 | queue->size = 0; 46 | queue->allSize = QUEUE_SIZE; 47 | 48 | return queue; 49 | } 50 | 51 | // 将请求添加到队列中 52 | void queue_append(queue_t *queue,void *value) 53 | { 54 | if(queue->size == queue->allSize) 55 | { 56 | queue->allSize = queue->allSize * 2; //如果队列满了,则2倍空间重新分配内存 57 | queue->buf = (void **)realloc(queue->buf,queue->allSize * sizeof(void*)); 58 | } 59 | queue->buf[queue->size] = value; 60 | queue->size++; 61 | } 62 | 63 | // 目前队列里request headers的个数 64 | unsigned int queue_size(queue_t* queue) 65 | { 66 | return queue->size; 67 | } 68 | 69 | // 释放队列和队列里申请的内存 70 | void free_queue(queue_t* queue) 71 | { 72 | //free(queue->buf); 73 | free(queue); 74 | } 75 | 76 | // 返回队列里指定第id个request header 77 | void *queue_at(queue_t* queue,unsigned int id) 78 | { 79 | if(id >= queue->size) 80 | { 81 | return NULL; 82 | } 83 | return queue->buf[id]; 84 | 85 | } 86 | 87 | // 释放队列内容 88 | void delete_queue(queue_t* queue) 89 | { 90 | unsigned int i; 91 | for(i = 0; i < queue_size(queue);++i) 92 | { 93 | free(queue_at(queue,i)); 94 | } 95 | 96 | free_queue(queue); 97 | } 98 | 99 | // 将十六进制数,比如b转为对应的数11 100 | char from_hex(char c) 101 | { 102 | return isdigit(c) ? c - '0' : tolower(c) - 'a' + 10; 103 | } 104 | 105 | // 将Response格式化输出到fd 106 | void generic_response(FILE* fd,char* status,char* message) 107 | { 108 | fprintf(fd,"HTTP/1.1 %s\r\n" 109 | "Content-Type: text/plain\r\n" 110 | "Content-Length: %d\r\n" 111 | "\r\n" 112 | "%s\r\n",status,strlen(message),message); 113 | } 114 | 115 | // 关闭管道并且等待子进程,防止产生僵尸进程 116 | void* wait_pid(void* data) 117 | { 118 | struct cgi_data* cgi_d = (struct cgi_data*)data; 119 | // 等待进程号为cgi->pid子进程完成,防止产生僵尸进程 120 | int status; 121 | waitpid(cgi_d->pid,&status,0); 122 | 123 | // 关闭管道 124 | close(cgi_d->fd); 125 | close(cgi_d->fd2); 126 | return NULL; 127 | } 128 | 129 | // 不支持的HTTP方法请求处理 130 | void unsupport(struct socket_request* socket,queue_t* queue,FILE* fd,request_h* request_header) 131 | { 132 | generic_response(fd,"501 Not Implemented","Not implemented: The request type was not understood by the server.\n"); 133 | delete_queue(queue); 134 | disconnect(socket,fd,request_header); 135 | } 136 | 137 | // 处理request header,判断请求类型 138 | int processHeader(struct socket_request* socket,queue_t* queue,FILE* fd,int* type_width,request_h* request_header) 139 | { 140 | unsigned int i; 141 | for(i = 0; i < queue_size(queue); ++i) 142 | { 143 | char *str = (char*)(queue_at(queue,i)); 144 | //查找header里的: 145 | char* colon = strstr(str,": "); 146 | // 如果不存在: 且不为第一行,则处理请求方式 147 | if(!colon) 148 | { 149 | // 处理Reqest Line 150 | if(i > 0) 151 | { 152 | generic_response(fd,"400 Bad Request","Bad Request: A header line was missing colon."); 153 | delete_queue(queue); 154 | disconnect(socket,fd,request_header); 155 | return -1; 156 | } 157 | // 根据首字节,判断HTTP 请求方式,即GET,POST等等 158 | switch(str[0]) 159 | { 160 | case 'G': 161 | if(strstr(str,"GET ") == str) 162 | { 163 | *type_width = 4; 164 | request_header->request_type = 1; 165 | } 166 | else 167 | { 168 | // 没有发现GET 类型,跳到不支持的类型处理函数,下同理 169 | unsupport(socket,queue,fd,request_header); 170 | return -1; 171 | } 172 | break; 173 | case 'P': 174 | if(strstr(str,"POST ") == str) 175 | { 176 | // POST 加上空格,为5个字节,向后移动5字节,下同 177 | *type_width = 5; 178 | request_header->request_type = 2; 179 | } 180 | else 181 | { 182 | unsupport(socket,queue,fd,request_header); 183 | return -1; 184 | } 185 | break; 186 | case 'H': 187 | if(strstr(str,"HEAD ") == str) 188 | { 189 | *type_width = 5; 190 | request_header->request_type = 3; 191 | } 192 | else 193 | { 194 | unsupport(socket,queue,fd,request_header); 195 | } 196 | break; 197 | default: 198 | unsupport(socket,queue,fd,request_header); 199 | return -1; 200 | //break; 201 | } 202 | 203 | // 向后移动字节 204 | request_header->filename = str + (*type_width); 205 | 206 | // 网站首页一般为 '\',其他为网址后的URI 207 | if(request_header->filename[0] == ' ' || request_header->filename[0] == '\r' || request_header->filename[0] == '\n') 208 | { 209 | generic_response(fd,"400 Bad Request","Bad Request: No File.\n"); 210 | delete_queue(queue); 211 | disconnect(socket,fd,request_header); 212 | return -1; 213 | } 214 | 215 | // 判断HTTP的版本,有HTTP 1.0 和 HTTP1.1,现在多为1.1版本 216 | request_header->http_version = strstr(request_header->filename,"HTTP/"); 217 | if(!(request_header->http_version)) 218 | { 219 | // No Http Version 220 | generic_response(fd,"400 Bad Request","Bad Request: No HTTP Version.\n"); 221 | delete_queue(queue); 222 | disconnect(socket,fd,request_header); 223 | return -1; 224 | } 225 | 226 | // 得到filename的值 227 | request_header->http_version[-1] = '\0'; 228 | char *tmp; 229 | tmp = strstr(request_header->http_version,"\r\n"); 230 | if(tmp) 231 | { 232 | tmp[0] = '\0'; 233 | } 234 | tmp = strstr(request_header->http_version,"\n"); 235 | if(tmp) 236 | { 237 | tmp[0] = '\0'; 238 | } 239 | 240 | //获取查询的字符串,如http://xxx.com/?s=python 241 | request_header->querystring = strstr(request_header->filename,"?"); 242 | if(request_header->querystring) 243 | { 244 | request_header->querystring++; 245 | request_header->querystring[-1] = '\0'; 246 | } 247 | } 248 | else 249 | { 250 | // 如果第一行请求出现:,表示出错 251 | if(i == 0) 252 | { 253 | // Request Line 错误 254 | generic_response(fd,"400 Bad Request","Bad Request: First Line was not a Correct Request.\n"); 255 | delete_queue(queue); 256 | disconnect(socket,fd,request_header); 257 | return -1; 258 | } 259 | // 处理header,指针移动向后移动两位,指向header各响应值 260 | colon[0] = '\0'; 261 | colon = colon + 2; 262 | 263 | // 判断是否包含此请求类型 264 | if(strcmp(str,"Host") == 0) 265 | { 266 | request_header->host = colon; 267 | } 268 | else if(strcmp(str,"Content-Type") == 0) 269 | { 270 | // MIME-Type Message 271 | request_header->c_type = colon; 272 | } 273 | else if(strcmp(str,"Cookie") == 0) 274 | { 275 | request_header->c_cookie = colon; 276 | } 277 | else if(strcmp(str,"User-Agent") == 0) 278 | { 279 | request_header->c_uagent = colon; 280 | } 281 | else if(strcmp(str,"Content-Length") == 0) 282 | { 283 | request_header->c_length = atol(colon); 284 | } 285 | else if(strcmp(str,"Referer") == 0) 286 | { 287 | request_header->c_referer = colon; 288 | } 289 | 290 | } 291 | 292 | } 293 | 294 | // 如果HTTP请求类型不存在 295 | if(!(request_header->request_type)) 296 | { 297 | unsupport(socket,queue,fd,request_header); 298 | return -1; 299 | } 300 | return 0; 301 | } 302 | 303 | // 解析文件和URL编码 304 | int request_file(struct socket_request* socket,queue_t* queue,FILE* fd,request_h* request_header) 305 | { 306 | // 如果请求地址URI为空,或者包含' 空格等特殊字符,则请求失败 307 | //printf("%s\n %s\n",request_header->querystring,request_header->filename); 308 | if(!(request_header->filename) || strstr(request_header->filename,"'") || strstr(request_header->filename," ") || (request_header->querystring && strstr(request_header->querystring," "))) 309 | { 310 | generic_response(fd,"400 Bad Request","Bad Request: Filename was Error!"); 311 | delete_queue(queue); 312 | disconnect(socket,fd,request_header); 313 | return -1; 314 | } 315 | 316 | request_header->_filename = calloc(sizeof(char) * (strlen(PAGES) + strlen(request_header->filename) + 2),1); 317 | // 连接pages + 文件名,strcat返回首地址 318 | strcat(request_header->_filename,PAGES); 319 | strcat(request_header->_filename,request_header->filename); 320 | 321 | // 如果文件名+目录里包含 % 字符,表示为中文,需要作URL解码 322 | if(strstr(request_header->_filename,"%")) 323 | { 324 | char buf[1024] = {0}; 325 | char *pstr = request_header->_filename; 326 | char *pbuf = buf; 327 | while(*pstr) 328 | { 329 | if(*pstr == '%') 330 | { 331 | if(pstr[1] && pstr[2]) 332 | { 333 | *pbuf++ = from_hex(pstr[1]) << 4 | from_hex(pstr[2]); 334 | pstr += 2; 335 | } 336 | } 337 | else if(*pstr == '+') 338 | { 339 | *pbuf++ = ' '; 340 | } 341 | else 342 | { 343 | *pbuf++ = *pstr; 344 | } 345 | pstr++; 346 | } 347 | *pbuf = '\0'; 348 | request_header->_filename = (char*)buf; 349 | } 350 | //printf("request_header->filename:%s\n",request_header->filename); 351 | // 获取文件后缀 352 | request_header->ext = request_header->filename + 1; 353 | // 循环得出最后的文件后缀开始 354 | while(strstr(request_header->ext + 1,".")) 355 | { 356 | request_header->ext = strstr(request_header->ext + 1,"."); 357 | } 358 | if(request_header->ext == request_header->filename + 1) 359 | { 360 | 361 | request_header->ext = NULL; 362 | } 363 | 364 | return 0; 365 | } 366 | 367 | // 设置CGI环境变量 368 | int set_env(struct socket_request* socket,queue_t* queue,request_h* request_header) 369 | { 370 | if(!(request_header->host)) 371 | { 372 | char hostname[1024]; 373 | gethostname(hostname,sizeof(hostname)); 374 | setenv("SERVER_NAME",hostname,1); 375 | setenv("HTTP_HOST",hostname,1); 376 | } 377 | else 378 | { 379 | setenv("SERVER_NAME",request_header->host,1); 380 | setenv("HTTP_HOST",request_header->host,1); 381 | } 382 | setenv("GATEWAY_INTERFACE","CGI/1.0",1); 383 | setenv("SERVER_PROTOCOL","HTTP/1.1",1); 384 | char c_port[20]; 385 | sprintf(c_port,"%d",port); 386 | setenv("SERVER_PORT",c_port,1); 387 | // 设置请求类型 388 | if(request_header->request_type == 1) 389 | { 390 | setenv("REQUEST_METHOD","GET",1); 391 | } 392 | else if(request_header->request_type == 2) 393 | { 394 | setenv("REQUEST_METHOD","POST",1); 395 | } 396 | else if(request_header->request_type == 3) 397 | { 398 | setenv("REQUEST_METHOD","HEAD",1); 399 | } 400 | // URL中查询字符串,即问号后面那个 401 | if(request_header->querystring) 402 | { 403 | if(strlen(request_header->querystring)) 404 | { 405 | setenv("QUERY_STRING",request_header->querystring,1); 406 | } 407 | else 408 | { 409 | setenv("QUERY_STRING","",1); 410 | } 411 | } 412 | // 去掉之前目录,获得单文件名字 413 | while(strstr(request_header->_filename,"/")) 414 | { 415 | request_header->_filename = strstr(request_header->_filename,"/") + 1; 416 | } 417 | request_header->_filename[-1] = '\0'; 418 | char fullpath[1024 + strlen(request_header->_filename)]; 419 | getcwd(fullpath,1023); 420 | strcat(fullpath,"/"PAGES"/"); 421 | //fprintf(stderr,"%s\n",request_header->filename); 422 | strcat(fullpath,request_header->_filename); 423 | setenv("PATH_TRANSLATED",fullpath,1); 424 | setenv("SCRIPT_NAME",request_header->filename,1); 425 | setenv("SCRIPT_FILENAME",request_header->_filename,1); 426 | setenv("REDIRECT_STATUS","200",1); 427 | char c_length[100]; 428 | c_length[0] = '\0'; 429 | sprintf(c_length,"%lu",request_header->c_length); 430 | setenv("CONTENT_LENGTH",c_length,1); 431 | if(request_header->c_type) 432 | { 433 | setenv("CONTENT_TYPE",request_header->c_type,1); 434 | } 435 | // 获取远程客户端地址和主机名字 436 | 437 | struct hostent* client; 438 | client = gethostbyaddr((const char*)&socket->remote_addr.sin_addr.s_addr,sizeof(socket->remote_addr.sin_addr.s_addr),AF_INET); 439 | setenv("REMOTE_HOST",client->h_name,1); 440 | //printf("k%s\n",client->h_name); 441 | 442 | //printf("%s\n",request_header->_filename); 443 | setenv("REMOTE_ADDR",inet_ntoa(socket->remote_addr.sin_addr),1); 444 | // 设置Cookie 445 | if(request_header->c_cookie) 446 | { 447 | setenv("HTTP_COOKIE",request_header->c_cookie,1); 448 | } 449 | // 设置 USER_AGENT 450 | if(request_header->c_uagent) 451 | { 452 | setenv("HTTP_USER_AGENT",request_header->c_uagent,1); 453 | } 454 | 455 | // 设置 Referer 456 | if(request_header->c_referer) 457 | { 458 | setenv("HTTP_REFERER",request_header->c_referer,1); 459 | } 460 | char execute[1024]; 461 | execute[0] = '\0'; 462 | //sprintf(execute,"./%s",request_header->_filename); 463 | sprintf(execute,"%s",fullpath); 464 | // execlp 从PATH 环境变量中查找文件并执行,执行成功则不返回,否则返回-1 465 | execlp(execute,execute,(char*)0); 466 | // 如果执行失败,则继续下面操作 467 | fprintf(stderr,"[warn] Failed to execute CGI Script:%s?%s.\n",fullpath,request_header->querystring); 468 | // 回收内存 469 | delete_queue(queue); 470 | pthread_detach(socket->thread); 471 | free(socket); 472 | 473 | return -1; 474 | } 475 | 476 | 477 | // 判断_filename 是不是目录 478 | int check_dir(FILE* fd,request_h* request_header,struct stat* Stats) 479 | { 480 | if(request_header->_filename[strlen(request_header->_filename) - 1] != '/') 481 | { 482 | // 目录结尾无 '/' 483 | fprintf(fd,"HTTP/1.1 301 Moved Permanently\r\n"); 484 | fprintf(fd,"Location: %s/\r\n",request_header->filename); 485 | fprintf(fd,"Content-Length: 0\r\n\r\n"); 486 | 487 | return -1; 488 | } 489 | else 490 | { 491 | // 无默认文件,列出目录列表 492 | struct dirent **files = {0}; 493 | int filecount = -1; 494 | filecount = scandir(request_header->_filename,&files,0,alphasort); 495 | // 打印目录列表,先发送HTTP Response 496 | fprintf(fd,"HTTP/1.1 200 OK\r\n"); 497 | fprintf(fd,"Content-Type: text/html\r\n"); 498 | 499 | // 为显示的HTML内容分配一些内存 500 | char *html = malloc(1024); 501 | html[0] = '\0'; 502 | strcat(html,"Directory Listing"); 503 | // 打印列表内容,并构造html 504 | unsigned int i; 505 | for(i = 0; i < filecount; ++i) 506 | { 507 | char fullname[strlen(request_header->_filename) + 1 + strlen(files[i]->d_name) + 1]; 508 | sprintf(fullname,"%s/%s",request_header->_filename,files[i]->d_name); 509 | // 忽略目录下的目录 510 | if(stat(fullname,&(*Stats)) == 0 && S_ISDIR((*Stats).st_mode)) 511 | { 512 | free(files[i]); 513 | continue; 514 | } 515 | 516 | char _file[2 * strlen(files[i]->d_name) + 64]; 517 | sprintf(_file,"%s
\n",files[i]->d_name,files[i]->d_name); 518 | // 重新分配内存,将上述内容添加到末尾 519 | html = realloc(html,strlen(html) + strlen(_file) + 1); 520 | strcat(html,_file); 521 | free(files[i]); 522 | } 523 | free(files); 524 | html = realloc(html,strlen(html) + 64); 525 | strcat(html,""); 526 | 527 | // 发送Response 528 | fprintf(fd,"Content-Length: %d\r\n",(sizeof(char) * strlen(html))); 529 | fprintf(fd,"\r\n"); 530 | fprintf(fd,"%s",html); 531 | free(html); 532 | } 533 | return 0; 534 | } 535 | 536 | // 确定MIME类型,根据请求类型发送Response 537 | int determine_mime(queue_t* queue,FILE* fd,FILE* content,request_h* request_header) 538 | { 539 | if(request_header->ext) 540 | { 541 | // 判断请求文件类型,对不同的请求,Response不同的值 542 | if(!strcmp(request_header->ext,".htm") || !strcmp(request_header->ext,".html")) 543 | { 544 | fprintf(fd,"Content-Type: text/html\r\n"); 545 | } 546 | else if(!strcmp(request_header->ext,".css")) 547 | { 548 | fprintf(fd,"Content-Type: text/css\r\n"); 549 | } 550 | else if(!strcmp(request_header->ext,".png")) 551 | { 552 | fprintf(fd,"Content-Type: image/png\r\n"); 553 | } 554 | else if(!strcmp(request_header->ext,".jpg")) 555 | { 556 | fprintf(fd,"Content-Type: image/jpeg\r\n"); 557 | } 558 | else if(!strcmp(request_header->ext,".gif")) 559 | { 560 | fprintf(fd,"Content-Type: image/gif\r\n"); 561 | } 562 | else if(!strcmp(request_header->ext,".pdf")) 563 | { 564 | fprintf(fd,"Content-Type: application/pdf\r\n"); 565 | } 566 | // HTML5里面的东西,好像是做缓存用的 567 | else if(!strcmp(request_header->ext,".manifest")) 568 | { 569 | fprintf(fd,"Content-Type: text/cache-manifest\r\n"); 570 | } 571 | else 572 | { 573 | fprintf(fd,"Content-Type: text/unknown\r\n"); 574 | } 575 | } 576 | else 577 | { 578 | fprintf(fd,"Content-Type: text/unknown\r\n"); 579 | } 580 | 581 | // HEAD request,只需要读取headers 582 | if(request_header->request_type == 3) 583 | { 584 | fprintf(fd,"\r\n"); 585 | fclose(content); 586 | fflush(fd); 587 | delete_queue(queue); 588 | 589 | return 1; 590 | } 591 | 592 | fseek((content),0,SEEK_END); 593 | long size = ftell(content); 594 | fseek((content),0,SEEK_SET); 595 | 596 | // 发送出去header后的文本长度 597 | fprintf(fd,"Content-Length: %ld\r\n",size); 598 | fprintf(fd,"\r\n"); 599 | 600 | char buf[HTML_SIZE]; 601 | while(!feof(content)) 602 | { 603 | size_t n = fread(buf,1,HTML_SIZE - 1,content); 604 | fwrite(buf,1,n,fd); 605 | } 606 | fprintf(fd,"\r\n"); 607 | fclose(content); 608 | return 0; 609 | } 610 | 611 | // 父进程读取客户端动态脚本传来的数据(Form表单数据),通过管道传入到子进程 612 | void pipe_trans(queue_t* queue,FILE* fd,FILE* cgi_w,request_h* request_header) 613 | { 614 | // 读取客户端数据,并通过管道传入到子进程,传输POST数据 615 | if(request_header->c_length > 0) 616 | { 617 | size_t total = 0; 618 | char buf[CGI_POST]; 619 | while((total < request_header->c_length) && (!feof(fd))) 620 | { 621 | size_t left = request_header->c_length - total; 622 | if(left > CGI_POST) 623 | { 624 | // 请求太大 625 | left = CGI_POST; 626 | } 627 | size_t n = fread(buf,1,left,fd); 628 | total += n; 629 | 630 | // 向管道写入数据 631 | fwrite(buf,1,n,cgi_w); 632 | } 633 | } 634 | 635 | if(cgi_w) 636 | { 637 | fclose(cgi_w); 638 | } 639 | 640 | } 641 | 642 | // 子进程通过管道将数据传送过来,父进程解析HTTP头 643 | int read_header(struct socket_request* socket,queue_t* queue,FILE* fd,FILE* cgi_r,request_h* request_header,pthread_t* waitthread) 644 | { 645 | char buf[HEADER_SIZE]; 646 | if(!cgi_r) 647 | { 648 | generic_response(fd,"500 Internal Server Error","Failed to execute CGI Script.\n"); 649 | pthread_detach(*waitthread); 650 | fflush(fd); 651 | delete_queue(queue); 652 | return 1; 653 | } 654 | fprintf(fd,"HTTP/1.1 200 OK\r\n"); 655 | unsigned int i = 0; 656 | 657 | // 读取子进程通过管道传输的数据,并写到socket中 658 | while(!feof(cgi_r)) 659 | { 660 | char* in = fgets(buf,HEADER_SIZE - 1,cgi_r); 661 | if(!in) 662 | { 663 | fprintf(stderr,"[warn] Read nothing [%d on %p].\n",ferror(cgi_r),cgi_r); 664 | buf[0] = '\0'; 665 | break; 666 | } 667 | 668 | if(!strcmp(in,"\r\n") || !strcmp(in,"\n")) 669 | { 670 | buf[0] = '\0'; 671 | break; 672 | } 673 | 674 | if(!strcmp(in,": ") && !strcmp(in,"\r\n")) 675 | { 676 | fprintf(stderr,"[warn] Reuqest Line was too long or Error %zu.\n",strlen(buf)); 677 | break; 678 | } 679 | 680 | fwrite(in,1,strlen(in),fd); 681 | ++i; 682 | } 683 | if(i < 1) 684 | { 685 | fprintf(stderr,"[warn] CGI Script didn't give us headers.\n"); 686 | } 687 | 688 | 689 | if(feof(cgi_r)) 690 | { 691 | fprintf(stderr,"[warn] The End of File,May be the Pipe is closed.\n"); 692 | } 693 | 694 | // HEAD请求,只请求页面的首部 695 | if(request_header->request_type == 3) 696 | { 697 | fprintf(fd,"\r\n"); 698 | pthread_detach(*waitthread); 699 | fflush(fd); 700 | delete_queue(queue); 701 | return 1; 702 | } 703 | 704 | int mode = 0; 705 | // 如果HTTP协议为HTTP/1.1,则设置Transfer-Encodingchunked 706 | // 这样我们可以分块发送,而不用一次全部发送 707 | if(!strcmp(request_header->http_version,"HTTP/1.1")) 708 | { 709 | fprintf(fd,"Transfer-Encoding: chunked\r\n"); 710 | } 711 | else 712 | { 713 | // 如果不是HTTP/1.1,即为HTTP/1.0, 714 | // 则没实现长链接,Connection应设置为close 715 | fprintf(fd,"Connection: close\r\n\r\n"); 716 | mode = 1; 717 | } 718 | 719 | 720 | if(strlen(buf) > 0) 721 | { 722 | fprintf(stderr,"[warn] Trying to dump remaing content.\n"); 723 | // 其内容为一个chunk,用CRLF隔开,即\r\n 724 | // ASCII值打印 725 | fprintf(fd,"\r\n%zX\r\n",strlen(buf)); 726 | fwrite(buf,1,strlen(buf),fd); 727 | } 728 | 729 | // 继续从子进程的CGI 应用程序读取数据 730 | // 如果为HTTP/1.1,则以chunks形式发送 731 | while(!feof(cgi_r)) 732 | { 733 | size_t n_read = -1; 734 | n_read = fread(buf,1,HEADER_SIZE-1,cgi_r); 735 | if(n_read < 1) 736 | { 737 | fprintf(stderr,"[warn] Read nothing from CGI Application.\n"); 738 | break; 739 | } 740 | // 如果为HTTP/1.1 741 | if(mode == 0) 742 | { 743 | fprintf(fd,"\r\n%zX\r\n",n_read); 744 | } 745 | fwrite(buf,1,n_read,fd); 746 | } 747 | 748 | if(mode == 0) 749 | { 750 | // 使用 0 字节长度的块结束chunked 751 | fprintf(fd,"\r\n0\r\n\r\n"); 752 | } 753 | 754 | pthread_detach(*waitthread); 755 | if(cgi_r) 756 | { 757 | fclose(cgi_r); 758 | } 759 | 760 | // 如果是HTTP/1.1就保持长链接,不断开,只需要释放内存,继续while循环即可 761 | if(mode == 0) 762 | { 763 | fflush(fd); 764 | delete_queue(queue); 765 | 766 | return -1; 767 | } 768 | // 如果是HTTP/1.0,断开链接 769 | else 770 | { 771 | delete_queue(queue); 772 | disconnect(socket,fd,request_header); 773 | return -1; 774 | } 775 | 776 | } 777 | 778 | int parse_html(struct socket_request* socket,queue_t* queue,FILE* fd,request_h* request_header) 779 | { 780 | //printf("%s\n",request_header->_filename); 781 | FILE *content = fopen(request_header->_filename,"rb"); 782 | // 如果打不开文件,显示404错误,或者也可能是403权限问题,这里用400表示 783 | if(!content) 784 | { 785 | generic_response(fd,"400 Bad Request","Bad Request: No File Or Forbidden.\n"); 786 | fflush(fd); 787 | delete_queue(queue); 788 | // 此处。。。 free _filename 789 | // disconnect(socket,fd,request_header); 790 | // 此处有问题 791 | return 1; 792 | } 793 | else 794 | { 795 | struct stat Stats; 796 | // 判断文件是否可执行 797 | if(stat(request_header->_filename,&Stats) == 0 && (Stats.st_mode & S_IXOTH)) 798 | { 799 | fclose(content); 800 | 801 | // 使用双向管道 802 | int pipe_r[2]; 803 | int pipe_w[2]; 804 | if(pipe(pipe_r) < 0) 805 | { 806 | fprintf(stderr,"Failed to create read pipe!\n"); 807 | } 808 | if(pipe(pipe_w) < 0) 809 | { 810 | fprintf(stderr,"Failed to create write pipe!\n"); 811 | } 812 | // 使用多进程,父子进程 813 | pid_t pid = 0; 814 | pid = fork(); 815 | 816 | // 子进程 817 | if(pid == 0) 818 | { 819 | // 重定向 820 | dup2(pipe_r[0],STDIN_FILENO); 821 | dup2(pipe_w[1],STDOUT_FILENO); 822 | // 关闭 823 | close(pipe_r[1]); 824 | close(pipe_w[0]); 825 | // 已重定向,向STDOUT发送消息,即通过管道向父进程发送消息 826 | // 控制缓存的时间,即立即过期 827 | fprintf(stdout,"Expires: -1\r\n"); 828 | // 进入www目录下 829 | char* dir = request_header->_filename; 830 | char wwwroot[1024]; 831 | getcwd(wwwroot,1024); 832 | strcat(wwwroot,"/"PAGES); 833 | chdir(dir); 834 | // 设置DOCUMENT_ROOT 环境变量,嫌麻烦,没有统一处理,还得传参 835 | setenv("DOCUMENT_ROOT",wwwroot,1); 836 | int id = set_env(socket,queue,request_header); 837 | if(id == -1) 838 | { 839 | // 即子进程脚本没有执行 840 | return -1; 841 | } 842 | } 843 | 844 | // 父进程 845 | struct cgi_data* cgi_d = malloc(sizeof(struct cgi_data)); 846 | cgi_d->pid = pid; 847 | // 待关闭 848 | cgi_d->fd = pipe_w[1]; 849 | cgi_d->fd2 = pipe_r[0]; 850 | pthread_t waitthread; 851 | // 创建一个线程,关闭父进程里的文件描述符,使其成为双向管道 852 | pthread_create(&waitthread,NULL,wait_pid,(void*)(cgi_d)); 853 | 854 | // cgi_r 读取CGI 程序的输出,cgi_w映射到CGI程序的标准输入 855 | FILE* cgi_r = fdopen(pipe_w[0],"r"); 856 | FILE* cgi_w = fdopen(pipe_r[1],"w"); 857 | 858 | // 管道 859 | pipe_trans(queue,fd,cgi_w,request_header); 860 | 861 | // 读取header 862 | int id = read_header(socket,queue,fd,cgi_r,request_header,&waitthread); 863 | if(id == 1) 864 | { 865 | return 1; 866 | } 867 | else if(id == -1) 868 | { 869 | return -1; 870 | } 871 | 872 | } 873 | fprintf(fd,"HTTP/1.1 200 OK\r\n"); 874 | } 875 | // 确定MIME类型 876 | if((determine_mime(queue,fd,content,request_header)) == 1) 877 | { 878 | return 1; 879 | } 880 | else 881 | { 882 | return 0; 883 | } 884 | 885 | return 0; 886 | } 887 | 888 | // 将 request headers 放到队列当中,直到客户端断开连接 889 | int readRequest(struct socket_request* socket,queue_t* queue,FILE *fd,request_h* request_header) 890 | { 891 | char buf[HEADER_SIZE]; 892 | while(!feof(fd)) 893 | { 894 | // 当读到一个换行符或EOF,结束读取 895 | char* in = fgets(buf,HEADER_SIZE - 2,fd); 896 | // 到达文件末尾 897 | if(!in) 898 | { 899 | break; 900 | } 901 | 902 | // 到达headers末尾 903 | if(!strcmp(in,"\r\n") || !strcmp(in,"\n")) 904 | { 905 | break; 906 | } 907 | 908 | // request line后面有个\n,判断是否存在request line 909 | if(!strstr(in,"\n")) 910 | { 911 | generic_response(fd,"400 Bad Request","Bad Request: Request Line was too long.\n"); 912 | delete_queue(queue); 913 | disconnect(socket,fd,request_header); 914 | return -1; 915 | } 916 | 917 | char* request_line = malloc((strlen(buf)+1) * sizeof(char)); 918 | strcpy(request_line,buf); 919 | // 将request line 存储到queue里 920 | //printf("%s\n",request_line); 921 | queue_append(queue,(void*)request_line); 922 | } 923 | // Socket无文件结束标志,如果出现这个表示客户端关闭连接 924 | if(feof(fd)) 925 | { 926 | delete_queue(queue); 927 | disconnect(socket,fd,request_header); 928 | return -1; 929 | } 930 | return 0; 931 | } 932 | 933 | // 线程处理函数,处理每一个连接请求 934 | void *handleRequest(void *socket) 935 | { 936 | // GET POST 占的宽度 937 | int type_width = 0; 938 | 939 | request_h *request_header = (request_h*)malloc(sizeof(struct _request_h)); 940 | request_header->filename = NULL; 941 | request_header->querystring = NULL; 942 | request_header->request_type = 0; 943 | request_header->_filename = NULL; 944 | request_header->ext = NULL; 945 | request_header->host = NULL; 946 | request_header->http_version = NULL; 947 | request_header->c_length = 0; 948 | request_header->c_type = NULL; 949 | request_header->c_cookie = NULL; 950 | request_header->c_uagent = NULL; 951 | request_header->c_referer = NULL; 952 | 953 | struct socket_request *request = (struct socket_request*)socket; 954 | // 将socket文件描述符转换为标准的文件描述符,因为socket描述符不能使用标准I/O fopen打开 955 | FILE *fd = fdopen(request->fd,"r+"); 956 | if(!fd) 957 | { 958 | fprintf(stderr,"Transfer the Socket fd to File Error!\n"); 959 | disconnect(request,fd,request_header); 960 | return NULL; 961 | } 962 | 963 | // 读取requests请求,直到客户端断开连接 964 | while(1) 965 | { 966 | queue_t *queue = alloc_queue(); 967 | 968 | // 读取request请求 969 | if((readRequest(request,queue,fd,request_header)) == -1) 970 | { 971 | return NULL; 972 | } 973 | // 处理headers 974 | if ((processHeader(request,queue,fd,&type_width,request_header)) == -1) 975 | { 976 | return NULL; 977 | } 978 | //printf("%s\n%s\n%s\n",request_header->_filename,request_header->http_version,request_header->ext); 979 | // 解析文件和编码 980 | if((request_file(request,queue,fd,request_header)) == -1) 981 | { 982 | return NULL; 983 | } 984 | 985 | // 判断是否是目录 986 | struct stat Stats; 987 | if(stat(request_header->_filename,&Stats) == 0 && S_ISDIR(Stats.st_mode)) 988 | { 989 | if(check_dir(fd,request_header,&Stats) == -1) 990 | { 991 | return NULL; 992 | } 993 | } 994 | else 995 | { 996 | // 解析HTML ,返回-1,表示错误,线程不在执行 997 | int id = parse_html(request,queue,fd,request_header); 998 | if(id == -1) 999 | { 1000 | return NULL; 1001 | } 1002 | // 返回1,表示长链接 1003 | else if(id == 1) 1004 | { 1005 | continue; 1006 | } 1007 | 1008 | } 1009 | } 1010 | 1011 | } 1012 | 1013 | 1014 | int main(int argc,char *argv[]) 1015 | { 1016 | port = PORT; 1017 | 1018 | // 如果命令行参数个数大于1,即改变默认端口号,则修改默认端口号 1019 | if(argc > 1) 1020 | { 1021 | port = atoi(argv[1]); 1022 | } 1023 | 1024 | // 初始化TCP Socket 1025 | struct sockaddr_in addr; 1026 | if((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0) 1027 | { 1028 | fprintf(stderr,"Create Socket Error!\n"); 1029 | return -1; 1030 | } 1031 | 1032 | bzero(&addr,sizeof(addr)); 1033 | addr.sin_family = AF_INET; 1034 | addr.sin_port = htons(port); 1035 | addr.sin_addr.s_addr = htonl(INADDR_ANY); 1036 | 1037 | // 设置socket关闭后,仍可继续重用该socket 1038 | int reuseaddr = 1; 1039 | if(setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,(const char*)&reuseaddr,sizeof(int)) < 0) 1040 | { 1041 | close(sockfd); 1042 | return -1; 1043 | } 1044 | 1045 | // Bind the socket 1046 | if(bind(sockfd,(struct sockaddr*)&addr,sizeof(addr)) < 0) 1047 | { 1048 | fprintf(stderr,"Failed to bind socket to the %d port!\n",port); 1049 | return -1; 1050 | } 1051 | 1052 | // 监听来自浏览器的请求 1053 | listen(sockfd,10); 1054 | 1055 | printf("The Cgi Server is Listening the %d port.\n",port); 1056 | 1057 | // 捕捉终端CTRL+C产生的SIGINT信号,使用我们自己的处理函数 1058 | signal(SIGINT,handleSignal); 1059 | 1060 | // 忽略SIGPIPE信号,否则会异常终止 1061 | signal(SIGPIPE,SIG_IGN); 1062 | 1063 | // 使用多线程,循环处理每一个accept请求 1064 | while(1) 1065 | { 1066 | struct socket_request *coming = calloc(sizeof(struct socket_request),1); 1067 | unsigned int len; 1068 | len = sizeof(coming->remote_addr); 1069 | coming->fd = accept(sockfd,(struct sockaddr*)&(coming->remote_addr),&len); 1070 | pthread_create(&(coming->thread),NULL,handleRequest,(void *)(coming)); 1071 | 1072 | } 1073 | 1074 | } 1075 | 1076 | --------------------------------------------------------------------------------