自己动手写HTTP服务--myhttpd
2013-03-26 23:01
176 查看
/** sol12.5.c ** ------------------------------------------------------------ A version of webserv that puts some typical CGI variables into the environment before calling exec is sol12.5.c. Three kinds of variables are demonstrated in this solution: variables about the server, variables sent as part of the http header, and variables about the client. ** ------------------------------------------------------------ ** ** * Version of webserv.c that includes some environment variables * when running CGI programs. * * The variables included for cgi programs are: * SERVER_SOFTWARE * SERVER_NAME * REMOTE_PORT * REMOTE_ADDR * REQUEST_URI * REQUEST_METHOD * HTTP_USER_AGENT * * This program uses the varlib.c system from the smsh.c * program in the shell Chapter. That system does most * of the filing and export work for us. * * usage: ws portnumber * features: supports the GET command only * runs in the current directory * forks a new child to handle each request * has MAJOR security holes, for demo purposes only * has many other weaknesses, but is a good start * * build: cc sol12.5.c socklib.c varlib.c -o sol12.5 * * By showing how easily we can use varlib.c to manage the * variables for the web server, we see in concrete terms * how much a web browser work a shell and OS. */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <errno.h> #include <time.h> #include <errno.h> #include "varlib.h" #include "socklib.h" #define oops(s,x) { perror(s); exit(x); } void setupvars(); void process_request(int , struct sockaddr_in *); void process_header(FILE *); void act_on_request(char *, char *, int ); void header( FILE *, char *); void cannot_do(int ); void do_404(char *, int ); int isadir(char *); int not_exist(char *); void do_ls(char *, int ); char * file_type(char *); int ends_in_cgi(char *); void do_exec( char *, int ); void do_cat(char *, int ); int main(int ac, char *av[]) { int sock, fd; int len; struct sockaddr_in clnt_addr; if ( ac == 1 ){ fprintf(stderr,"usage: ws portnum\n"); exit(1); } sock = make_server_socket( atoi(av[1]) ); if ( sock == -1 ) oops("socket", 2); setupvars(); /* main loop here */ while(1){ /* * wait for a call */ len = sizeof(struct sockaddr); fd = accept(sock, (struct sockaddr *)&clnt_addr, &len); /* * take a call and buffer it */ if ( fd >= 0 ){ process_request(fd, &clnt_addr); close(fd); } else perror("accept"); } return 0; } /* * load the environment table from environ and * set some fixed values that describe this host and server */ void setupvars() { char hostname[512]; VLstore("SERVER_SOFTWARE", "Simple-WebServer 0.2"); VLexport("SERVER_SOFTWARE"); gethostname(hostname, 512); VLstore("SERVER_NAME", hostname); VLexport("SERVER_NAME"); /* add here other fixed values that can be computed at startup */ } /* * A more general process_request than the earlier version * This one has to read the header and put some parts into * the environment list. */ void process_request(int fd, struct sockaddr_in *caller) { FILE *fpin; char request[BUFSIZ]; char portnumstr[10]; char cmd[BUFSIZ], arg[BUFSIZ]; /* process the request in a child process */ if ( fork() != 0 ) return; /* jot down the caller's adress and port */ VLstore("REMOTE_ADDR", inet_ntoa(caller->sin_addr)); VLexport("REMOTE_ADDR"); sprintf(portnumstr, "%d", ntohs(caller->sin_port)); VLstore("REMOTE_PORT", portnumstr); VLexport("REMOTE_PORT"); /* buffer the socket for input and read request */ fpin = fdopen(fd, "r" ); if ( fpin == NULL ) exit(2); fgets(request,BUFSIZ,fpin); printf("got a call: request = %s", request); process_header(fpin); /* then do the request */ strcpy(arg, "./"); if ( sscanf(request,"%s%s", cmd, arg+2) == 2 ){ VLstore("REQUEST_METHOD", cmd); VLexport("REQUEST_METHOD"); VLstore("REQUEST_URI", arg+2); VLexport("REQUEST_URI"); act_on_request(cmd, arg, fd); } } /* ------------------------------------------------------ * process_header(FILE *) after the HTTP request may be several lines of header information. These variables provide define the parameters of the request. The set of parameters is terminated by a blank line. This function looks for the header line marked User-Agent: name-of-browser And puts the value for that tag in the environment under HTTP_USER_AGENT. It's pretty easy to add other http variables to the CGI environment. ------------------------------------------------------ */ void process_header(FILE *fp) { char buf[BUFSIZ]; char browser_tag[] = "User-Agent: "; int b_taglen = strlen(browser_tag); while( fgets(buf,BUFSIZ,fp) != NULL && strcmp(buf,"\r\n") != 0 ) { printf("Header line: %s", buf); if ( strncmp(buf, browser_tag, b_taglen) == 0 ) { VLstore("HTTP_USER_AGENT",buf+b_taglen); VLexport("HTTP_USER_AGENT"); } } } /* ------------------------------------------------------ * act_on_request( char *cmd, char *arg, int fd ) do what the request asks for and write reply to fd if the request is the HTTP command: GET /foo/bar.html HTTP/1.0 then cmd is "GET" and arg is "/foo/bar.html" ------------------------------------------------------ */ void act_on_request(char *cmd, char *arg, int fd) { if ( strcmp(cmd,"GET") != 0 ) cannot_do(fd); else if ( not_exist( arg ) ) do_404(arg, fd ); else if ( isadir( arg ) ) do_ls( arg, fd ); else if ( ends_in_cgi( arg ) ) do_exec( arg, fd ); else do_cat( arg, fd ); } /* ------------------------------------------------------ * the reply header thing: all functions need one if content_type is NULL then don't send content type ------------------------------------------------------ */ void header( FILE *fp, char *content_type ) { fprintf(fp, "HTTP/1.0 200 OK\r\n"); if ( content_type ) fprintf(fp, "Content-type: %s\r\nServer: Myhttpd/0.1\r\n", content_type );//这里有修改标记自己的server版本号 } /* ------------------------------------------------------ * simple functions first: cannot_do(fd) unimplemented HTTP command and do_404(item,fd) no such object ------------------------------------------------------ */ void cannot_do(int fd) { FILE *fp = fdopen(fd,"w"); fprintf(fp, "HTTP/1.0 501 Not Implemented\r\n"); fprintf(fp, "Content-type: text/plain\r\n"); fprintf(fp, "\r\n"); fprintf(fp, "That command is not yet implemented\r\n"); fclose(fp); } void do_404(char *item, int fd) { FILE *fp = fdopen(fd,"w"); fprintf(fp, "HTTP/1.0 404 Not Found\r\n"); fprintf(fp, "Content-type: text/plain\r\n"); fprintf(fp, "\r\n"); fprintf(fp, "The item you requested: %s\r\nis not found\r\n", item); fclose(fp); } /* ------------------------------------------------------ * the directory listing section isadir() uses stat, not_exist() uses stat do_ls runs ls. It should not ------------------------------------------------------ */ int isadir(char *f) { struct stat info; return ( stat(f, &info) != -1 && S_ISDIR(info.st_mode) ); } int not_exist(char *f) { struct stat info; return( stat(f,&info) == -1 ); } void do_ls(char *dir, int fd) { FILE *fp ; fp = fdopen(fd,"w"); header(fp, "text/plain"); fprintf(fp,"\r\n"); fflush(fp); dup2(fd,1); dup2(fd,2); close(fd); execlp("ls","ls","-l",dir,NULL); perror(dir); exit(1); } /* ------------------------------------------------------ * the cgi stuff. function to check extension and one to run the program. ------------------------------------------------------ */ char * file_type(char *f) /* returns 'extension' of file */ { char *cp; if ( (cp = strrchr(f, '.' )) != NULL ) return cp+1; return ""; } int ends_in_cgi(char *f) { return ( strcmp( file_type(f), "cgi" ) == 0 ); } void do_exec( char *prog, int fd ) { extern char **environ; FILE *fp ; fp = fdopen(fd,"w"); header(fp, NULL); fflush(fp); dup2(fd, 1); dup2(fd, 2); close(fd); environ = VLtable2environ(); execl(prog,prog,NULL); perror(prog); } /* ------------------------------------------------------ * do_cat(filename,fd) sends back contents after a header ------------------------------------------------------ */ void do_cat(char *f, int fd) { char *extension = file_type(f); char *content = "text/plain"; FILE *fpsock, *fpfile; int c; if ( strcmp(extension,"html") == 0 ) content = "text/html"; else if ( strcmp(extension, "gif") == 0 ) content = "image/gif"; else if ( strcmp(extension, "jpg") == 0 ) content = "image/jpeg"; else if ( strcmp(extension, "jpeg") == 0 ) content = "image/jpeg"; fpsock = fdopen(fd, "w"); fpfile = fopen( f , "r"); if ( fpsock != NULL && fpfile != NULL ) { header( fpsock, content ); fprintf(fpsock, "\r\n"); while( (c = getc(fpfile) ) != EOF ) putc(c, fpsock); fclose(fpfile); fclose(fpsock); } exit(0); }
/* * socklib.c * * This file contains functions used lots when writing internet * client/server programs. The two main functions here are: * * make_server_socket( portnum ) returns a server socket * or -1 if error * make_server_socket_q(portnum,backlog) * * connect_to_server(char *hostname, int portnum) * returns a connected socket * or -1 if error */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <time.h> #include <strings.h> #include <arpa/inet.h> #define HOSTLEN 256 #define BACKLOG 1 int make_server_socket_q(int , int ); int make_server_socket(int portnum) { return make_server_socket_q(portnum, BACKLOG); } int make_server_socket_q(int portnum, int backlog) { struct sockaddr_in saddr; /* build our address here */ struct hostent *hp; /* this is part of our */ char hostname[HOSTLEN]; /* address */ int sock_id; /* the socket */ sock_id = socket(PF_INET, SOCK_STREAM, 0); /* get a socket */ if ( sock_id == -1 ) return -1; /** build address and bind it to socket **/ bzero((void *)&saddr, sizeof(saddr)); /* clear out struct */ if ( gethostname(hostname, HOSTLEN) == -1 ) /* where am I ? */ { perror("gethostname"); exit(1); } hp = gethostbyname(hostname); /* get info about host */ if ( hp == NULL ){ perror("Cannot get host"); exit(2); } /* fill in host part */ bcopy( (void *)hp->h_addr, (void *)&saddr.sin_addr, hp->h_length); saddr.sin_port = htons(portnum); /* fill in socket port */ saddr.sin_family = AF_INET ; /* fill in addr family */ saddr.sin_addr.s_addr = INADDR_ANY; /* 作为服务器,你要绑定【bind】到本地的IP地址上进行监听【listen】, 但是你的机器上可能有多块网卡,也就有多个IP地址,这时候你要选择绑定在哪个IP上面,如果指定为INADDR_ANY,那么系统将绑定默认的网卡【即IP地址】。 其中INADDR_ANY就是指定地址为0.0.0.0的地址,这个地址事实上表示不确定地址,或“所有地址”、“任意地址”。 INADDR_ANY,外部的client ask 从哪个server的地址进来都可以连接到80端口.*/ if ( bind(sock_id, (struct sockaddr *)&saddr, sizeof(saddr)) != 0 ) return -1; /** arrange for incoming calls **/ if ( listen(sock_id, backlog) != 0 ) return -1; return sock_id; } int connect_to_server(char *host, int portnum) { int sock; struct sockaddr_in servadd; /* the number to call */ struct hostent *hp; /* used to get number */ /** Step 1: Get a socket **/ sock = socket( AF_INET, SOCK_STREAM, 0 ); /* get a line */ if ( sock == -1 ) return -1; /** Step 2: connect to server **/ bzero( &servadd, sizeof(servadd) ); /* zero the address */ hp = gethostbyname( host ); /* lookup host's ip # */ if (hp == NULL) return -1; bcopy(hp->h_addr, (struct sockaddr *)&servadd.sin_addr, hp->h_length); servadd.sin_port = htons(portnum); /* fill in port number */ servadd.sin_family = AF_INET ; /* fill in socket type */ if ( connect(sock,(struct sockaddr *)&servadd, sizeof(servadd)) !=0) return -1; return sock; }注意需要加 saddr.sin_addr.s_addr = INADDR_ANY;不然只能用127.0.0.1不能用外网ip
/* * header file for socklib * socklib.h*/int make_server_socket_q(int , int );int make_server_socket(int );int connect_to_server(char *, int );
/* varlib.c * * a simple storage system to store name=value pairs * with facility to mark items as part of the environment * * interface: * VLstore( name, value ) returns 1 for 0k, 0 for no * VLlookup( name ) returns string or NULL if not there * VLlist() prints out current table * * environment-related functions * VLexport( name ) adds name to list of env vars * VLtable2environ() copy from table to environ * VLenviron2table() copy from environ to table * * details: * the table is stored as an array of structs that * contain a flag for `global' and a single string of * the form name=value. This allows EZ addition to the * environment. It makes searching pretty easy, as * long as you search for "name=" * */ #include <stdio.h> #include <stdlib.h> #include "varlib.h" #include <string.h> #define MAXVARS 200 /* a linked list would be nicer */ struct var { char *str; /* name=val string */ int global; /* a boolean */ }; static struct var tab[MAXVARS]; /* the table */ static char *new_string( char *, char *); /* private methods */ static struct var *find_item(char *, int); int VLstore( char *name, char *val ) /* * traverse list, if found, replace it, else add at end * since there is no delete, a blank one is a free one * return 1 if trouble, 0 if ok (like a command) */ { struct var *itemp; char *s; int rv = 1; /* find spot to put it and make new string */ if ((itemp=find_item(name,1))!=NULL && (s=new_string(name,val))!=NULL) { if ( itemp->str ) /* has a val? */ free(itemp->str); /* y: remove it */ itemp->str = s; rv = 0; /* ok! */ } return rv; } char * new_string( char *name, char *val ) /* * returns new string of form name=value or NULL on error */ { char *retval; retval = malloc( strlen(name) + strlen(val) + 2 ); if ( retval != NULL ) sprintf(retval, "%s=%s", name, val ); return retval; } char * VLlookup( char *name ) /* * returns value of var or empty string if not there */ { struct var *itemp; if ( (itemp = find_item(name,0)) != NULL ) return itemp->str + 1 + strlen(name); return ""; } int VLexport( char *name ) /* * marks a var for export, adds it if not there * returns 1 for no, 0 for ok */ { struct var *itemp; int rv = 1; if ( (itemp = find_item(name,0)) != NULL ){ itemp->global = 1; rv = 0; } else if ( VLstore(name, "") == 1 ) rv = VLexport(name); return rv; } static struct var * find_item( char *name , int first_blank ) /* * searches table for an item * returns ptr to struct or NULL if not found * OR if (first_blank) then ptr to first blank one */ { int i; int len = strlen(name); char *s; for( i = 0 ; i<MAXVARS && tab[i].str != NULL ; i++ ) { s = tab[i].str; if ( strncmp(s,name,len) == 0 && s[len] == '=' ){ return &tab[i]; } } if ( i < MAXVARS && first_blank ) return &tab[i]; return NULL; } void VLlist() /* * performs the shell's `set' command * Lists the contents of the variable table, marking each * exported variable with the symbol '*' */ { int i; for(i = 0 ; i<MAXVARS && tab[i].str != NULL ; i++ ) { if ( tab[i].global ) printf(" * %s\n", tab[i].str); else printf(" %s\n", tab[i].str); } } int VLenviron2table(char *env[]) /* * initialize the variable table by loading array of strings * return 1 for ok, 0 for not ok */ { int i; char *newstring; for(i = 0 ; env[i] != NULL ; i++ ) { if ( i == MAXVARS ) return 0; newstring = malloc(1+strlen(env[i])); if ( newstring == NULL ) return 0; strcpy(newstring, env[i]); tab[i].str = newstring; tab[i].global = 1; } while( i < MAXVARS ){ /* I know we don't need this */ tab[i].str = NULL ; /* static globals are nulled */ tab[i++].global = 0; /* by default */ } return 1; } char ** VLtable2environ() /* * build an array of pointers suitable for making a new environment * note, you need to free() this when done to avoid memory leaks */ { int i, /* index */ j, /* another index */ n = 0; /* counter */ char **envtab; /* array of pointers */ /* * first, count the number of global variables */ for( i = 0 ; i<MAXVARS && tab[i].str != NULL ; i++ ) if ( tab[i].global == 1 ) n++; /* then, allocate space for that many variables */ envtab = (char **) malloc( (n+1) * sizeof(char *) ); if ( envtab == NULL ) return NULL; /* then, load the array with pointers */ for(i = 0, j = 0 ; i<MAXVARS && tab[i].str != NULL ; i++ ) if ( tab[i].global == 1 ) envtab[j++] = tab[i].str; envtab[j] = NULL; return envtab; }
/* * header for varlib.c package * varlib.h */ int VLenviron2table(char **); int VLexport(char *); char *VLlookup(char *); void VLlist(); int VLstore( char *, char * ); char **VLtable2environ(); int VLenviron2table(char **);
#cc socklib.c varlib.c sol12.5.c -o sol1
#./sol1 80
参考:《Unix Linux编程实践教程》第12章
相关文章推荐
- C语言动手实现一个自己的HttP服务
- 自己动手整合 Hessian 到 Hasor 中发布服务
- 【phpcms-v9】自己动手构建一个企业服务模块
- 自己动手学TCP/IP–http协议(http报文格式)
- 【原创】自己动手实现RPC服务调用框架
- 自己动手实现HTTP协议
- 自己动手学TCP/IP–http协议(http报文格式)
- 自己动手开发网络服务器(二):实现WSGI服务
- 自己动手实现简易代码生成器、采用文本模板文件生成服务层、服务层接口代码的做法参考
- 自己动手实现简易代码生成器、采用文本模板文件生成服务层、服务层接口代码的做法参考
- 自己动手实现简易代码生成器、采用文本模板文件生成服务层、服务层接口代码的做法参考
- 小程聊微服务-自己动手扩展分布式调用链
- 自己动手编写Web服务解析工具
- 【原创】自己动手写一个服务网关
- 自己动手学TCP/IP–http协议(http报文格式)
- 自己的服务器做网站服务,解决http被屏蔽、80端口穿透实现
- 自己动手写HTTP框架:异步任务篇
- 自己动手写HTTP框架:ThreadPool+Runnable+handler篇
- 自己动手编写Web服务工具[解析]
- 自己动手写Web容器之TomJetty之一:服务内功经脉