此项目适合二次开发,可以实现自己想实现的CGI应用程序。往下拉,是CGI部分,自己可以自行更改。
仅供参考!!!
mhttp.h
#ifndef _MHTTP_H_
#define _MHTTP_H_
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define MAX 1024
const char* status_line="HTTP/1.0 200 OK\r\n";
const char* blank_line="\r\n";
void DoConnect(int sock,int epollfd);
void hander_request(int sock,int epollfd);
void err_404(int sock);
int echo_www(int sock,const char* path,int size);
int exe_cgi(int sock,char *method,char *path,char *arg);
int startup(const char* ip,int port);
void SetNoblock(int sock);
void clear_header(int sock);
int getaline(int sock,char buf[],int size);
#endif
mhttp.c
#include"mhttp.h"
void SetNoblock(int fd){
int fl=fcntl(fd,F_GETFL);
if(fl<0){
perror("fcntl");
return;
}
fcntl(fd,F_SETFL,fl | O_NONBLOCK);
}
void DoConnect(int conn_fd,int epollfd){
struct sockaddr_in client;
socklen_t len=sizeof(client);
int listen_sock=accept(conn_fd,(struct sockaddr*)&client,&len);
printf("监听到client %d \n",listen_sock);
if(listen_sock<0){
perror("accept");
}
struct epoll_event conn_ev;
conn_ev.events=EPOLLIN;
conn_ev.data.fd=listen_sock;
int ret=epoll_ctl(epollfd,EPOLL_CTL_ADD,listen_sock,&conn_ev);
if(ret<0){
perror("epoll_ctl");
return;
}
return;
}
int startup(const char*ip,int port){
int sock=socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in local;
local.sin_family=AF_INET;
local.sin_addr.s_addr=inet_addr(ip);
local.sin_port=htons(port);
if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0){
perror("bind");
return -2;
}
if(listen(sock,10)<0){
perror("listen");
return -3;
}
return sock;
}
int getaline(int sock,char buf[],int size){
int i=0;
char c='\0';
while(i0){
if(c=='\r'){
recv(sock,&c,1,MSG_PEEK); //MSG_PEEK保留已读的字节
if(c=='\n'){
recv(sock,&c,1,0);//此处\r\n ->\n
}else{
c='\n';//\r ->\n
}
}
buf[i++]=c;
}else{
return -1;
}
}
buf[i]=0;
return i;
}
void clear_header(int sock){
char buf[MAX];
int ret=-1;
do{
ret=getaline(sock,buf,sizeof(buf));
}while(ret>0&&strcmp(buf,"\n")!=0);
}
int exe_cgi(int sock,char *method,char *path,char *arg){
char METHOD[MAX/10];
char QUERY_STRING[MAX];
char CONTENT_LENGTH[MAX];
int content_len=-1;
if(strcasecmp(method,"GET")==0){
clear_header(sock); //读完空行之前的数据
}
else{ //POST 方法
char buf[MAX];
int ret=-1;
while(1){
ret=getaline(sock,buf,sizeof(buf));
if(strncasecmp(buf,"Content-Length: ",16)==0){
content_len=atoi(&buf[16]);
}
if(1==strlen(buf)){
break;
}
}
if(content_len==-1){
err_404(sock);
return -1;
}
//getaline(sock,postbuf,content_len);
//printf("POST参数:%s\n",postbuf);
}
//如果是post已获得content length
//开始处理参数,并且响应
//这里用子进程处理参数,父进程将参数传给子进程,又子进程来完成cgi程序,父子进程间通信,使用管道。
int input[2];
int output[2];
if(pipe(input)<0|| pipe(output)<0){
err_404(404);
return -2;
}
send(sock,status_line,strlen(status_line),0);
send(sock,blank_line,strlen(blank_line),0);
int cpid=fork();
if(cpid<0){
perror("fork");
return -3;
}
if(cpid==0){ //子进程
close(input[1]);
close(output[0]);
sprintf(METHOD,"METHOD=%s",method);
putenv(METHOD);
if(strcasecmp(method,"GET")==0){
sprintf(QUERY_STRING,"QUERY_STRING=%s",arg);
putenv(QUERY_STRING);
}
else{//POST
printf("进入POST 传参\n");
sprintf(CONTENT_LENGTH,"CONTENT_LENGTH=%d",content_len);
putenv(CONTENT_LENGTH);
printf("传参完成\n");
}
dup2(input[0],0);
dup2(output[1],1);
//进程替换
execl(path,path,NULL);
exit(1);
}
else{ //父进程 通过管道传递数据
close(input[0]);
close(output[1]);
if(strcasecmp(method,"POST")==0){
int i=0;
char c='\0';
for(;i0){
send(sock,&c,1,0);
}else{
break;
}
}
waitpid(cpid,NULL,0);
close(input[1]);
close(output[0]);
}
return 0;
}
int echo_www(int sock,const char* path,int size){
printf("打开路径%s\n",path);
int fd=open(path,O_RDONLY);
if(fd<0){
perror("open");
return 404;
}
printf("打开文件描述符%d\n",fd);
send(sock,status_line,strlen(status_line),0);
send(sock,blank_line,strlen(blank_line),0);
printf("开始响应页面,页面大小%d\n",size);
sendfile(sock,fd,NULL,size);
close(fd);
return 200;
}
void err_404(int sock){
printf("开始错误解析:\n");
char err_path[1024]={0};
if(err_path[0]=='\0')
strcat(err_path,"mengtokaloroot/api/v1/err404.html");
printf("错误的路径: %s\n",err_path);
struct stat st;
stat(err_path,&st);
int size=st.st_size;
echo_www(sock,err_path,size);
}
void hander_request(int sock,int epollfd){
char buf[MAX];
char method[MAX/10];
char url[MAX];
char path[MAX];
int cgi=0;
char* query_string=NULL;
int errno_code=200;
size_t i=0,j=0;
if(getaline(sock,buf,sizeof(buf))<=0){
errno_code=404;
goto end;
}
printf("%s\n",buf);
while(i
main.c
#include"mhttp.h"
#include"mhttp.c"
//采用多路转接模型EPOLL来解决并发问题,工作模式为LT模式
int main(int argc,char*argv[]){
if(argc!=3){
printf("please input ip and port");
return 1;
}
int listen_sock=startup(argv[1],atoi(argv[2]));
int epoll_fd=epoll_create(10);
if(epoll_fd<0){
perror("epoll_create");
return 2;
}
SetNoblock(listen_sock);
struct epoll_event event;
event.events=EPOLLIN | EPOLLET;
event.data.fd=listen_sock;
if(epoll_ctl(epoll_fd,EPOLL_CTL_ADD,listen_sock,&event)<0){
perror("epoll_ctl");
return 3;
}
while(1){
struct epoll_event events[10];
int size=epoll_wait(epoll_fd,events,sizeof(events)/sizeof(events[0]),-1);
if(size<0){
perror("epoll_wait");
continue;
}
if(size==0){
printf("epoll timeout\n");
continue;
}
int i=0;
for(;i
根目录应该包含,服务器拥有的资源,以及相应的CGI程序等等。这题提供一个GET方法解析获得参数的CGI程序,和POST方法解析获得BODY里参数的CGI程序。
POST方法:
#include
#include"bookapi.h"
#include"bookapi.cpp"
int main(){
cout<<"BACK"<19){
cout<<"length of bookname is less then 19"<
GET方法: 相对简单,只需要使用环境变量就行。
#include
#include"bookapi.h"
#include"bookapi.cpp"
void urldecoder(char *p)
{
int i=0;
while(*(p+i))
{
if ((*p=*(p+i)) == '%')
{
*p=*(p+i+1) >= 'A' ? ((*(p+i+1) & 0XDF) - 'A') + 10 : (*(p+i+1) - '0');
*p=(*p) * 16;
*p+=*(p+i+2) >= 'A' ? ((*(p+i+2) & 0XDF) - 'A') + 10 : (*(p+i+2) - '0');
i+=2;
}
else if (*(p+i)=='+')
{
*p=' ';
}
p++;
}
*p='\0';
}
int main(){
cout<<"BACK"<19){
cout<<"
"< "<