HTTP项目,EPOLL版,包含GET/POST方法。

此项目适合二次开发,可以实现自己想实现的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<<"
"<"<

你可能感兴趣的:(HTTP项目,EPOLL版,包含GET/POST方法。)