簡易httpサーバです.
コードロジックから,簡単なhttpサーバロックに従うフレームワークと,バックグラウンド呼び出しのプロセスを明確に見ることができ,ここで,マルチプロセス,プロセス間通信のパイプ,およびネットワーク通信socketの知識を応用した.
/*************************************************************************
> File Name: httpd.c
> Author:Victor Qi
> Mail:[email protected]
> Created Time: Fri 25 Mar 2016 12:30:13 AM CST
************************************************************************/
#include//
#include//socket.h
#include// linux ,
#include// ,
#include
#include// , ,
#include//
#include// , , pid
#include//
#include//
#include// posix ,
#include//
#include//string.h
#define ISspace(x) isspace((int)(x))
#define SEVER_SIRING "sever:jdbhttpd/0.1.0\r
"
//
void accept_request(int);// http ,
void bad_request(int);// ,http 400
void cat(int,FILE*);// socket
void cannot_execute(int);// cgi 。
void error_die(const char *);// perror 。
void execute_cgi(int ,const char * ,const char * , const char *);// cgi ,
int get_line(int ,char *,int );// ,
void headers(int ,const char *);// http
void not_found(int);//
void sever_file(int ,const char *);// cat 。
int startup(u_short *);// httpd , , , 。
void unimplemented(int);// http method
// main -> startup -> accept_request -> execute_cgi.
// main()
int main(int argc ,char ** argv)
{
int sever_sock =-1;
u_short port =0;
int client_sock =-1;
struct sockaddr_in client_name;
int client_name_len =sizeof(client_name);
pthread_t newpthread;//
sever_sock = startup(&port);// httpd ,
print("httpd runing on port %d
",port);
for(;;)
{
//
client_sock=accept(sever_sock,(struct sockaddr *)&client_name,&client_name_len);// (struct sockaddr *)
if(client_sock==-1)
{
error_die("accept");
}
if(pthread_create(&newpthread,NULL,accept_request,client_sock)!=0)
perror("pthread_create failed");
}
close(sever_sock);
return 0;
}
// 。
void error_die(const char * error)
{
perror(error);
exit(1);
}
int startup(u_short *port)
{
int httpd=0;
struct sockaddr_in name;
if(httpd== -1)
{
error_die("socket error");
}
memset(&name,0,sizeof(name));
name.sin_family =AF_INET;
name.sin_port=htons(*port);
name.sin_addr.s_addr=htonl(INADDR_ANY);
if(bind(httpd,(struct sockaddr*)&name,sizeof(name))<0)
error_die("bind error");
if(*port==0)
{
int namelen=sizeof(name);
if(getsockname(httpd,(struct sockaddr *)&name,&namelen))//
{
error_die("getsockname error");
}
*port=ntons(name.sin_port);
}
if(listen(httpd,5)<0)
error_die("listen");
return httpd;
}
void accept_request(int client)
{
char buf[1024];
int numchar;
char method[255];
char url[255];
cahr path[512];
size_t i,j;
int cgi=0;
char *query_string=NULL;
//
numchar=get_line(client,buf,sizeof(buf));
i=0;j=0;
while(!ISspace(buf[j])&&(i<sizeof(method)-1))
{
method[i]=buf[j];
++i;
++j;
}
method[i]='\0';
//
if(strcasecmp(method,"GET")&&strcasecmp(method,"POST"))
{
unimplemented(client);
return ;
}
if(strcasecmp(method,"POST")==0)
{
cgi=1;// cgi
}
i=0;
while(ISspace(buf[j])&&(j<sizeof(buf)))
j++;
while(!ISspace(buf[j])&&(i<sizeof(url)-1)&&j<sizeof(buf))
{
// URL
url[i]=buf[j];
i++;
j++;
}
url[i]='\0';//
// get
if(strcasecmp(method,"GET")==0)
{
query_string=url;
// get ?
while((*query_string!='?')&&(*query_string!='\0'))
{
query_string++;
}
sprintf(path,"htdocs%s",url);// html htdocs
if(path[strlen(path)-1]=='/')
strcat(path,"index.html");
if(stat(path,&st)==-1)
{
// headers
while((numchar>0)&&strcmp("
",buf))
numchar=get_line(client,buf,sizeof(buf));
not_found(client);//
}
else
{
if((st.st_mode&S_IFMT)==S_IFDIR)
strcat(path,"index.html");
if((st.st_mode&S_IFUSR)||(st.s_mode&S_IXGRP)||(st.st_mode&S_IXOTH))
cgi=1;
// cgi, , cgi
if(!cgi)
{
sever_file(client,path);
}
else
execute_cgi(client,path,method,query_string);
}
close(client);//http :
}
}
void bad_request(int client)
{
char buf[1024];
// http
sprintf(buf,"HTTP/1.1 400 BAD REQUEST\r
");
send(client,buf,sizeof(buf),0);
sprintf(buf,"Content-type:text/html\r
");
send(client,buf,sizeof(buf),0);
sprintf(buf,"\r
");
send(client,buf,sizeof(buf),0);
sprintf(buf,"your brower send a bad request,"
);
send(client,buf,sizeof(buf),0);
sprintf(buf,"such as a POST without a Content-length.\t
");
send(client,buf,sizeof(buf),0);
}
void cat(int client,FILE*resourse)
{
char buf[1024];
// socket
fgets(buf,sizeof(buf),strlen(buf),0);
while(!feof(resourse))
{ //
send(client,buf,sizeof(buf),0);
fgets(buf,sizeof(buf),resourse);
}
}
void cannot_execute(int client)
{
//
sprintf(buf,"HTTP/1.1 500 Internal Sever Error\r
");
send(client,buf,strlen(buf),0);
sprintf(buf,"Content-type:text/html\r
");
send(client,buf,strlen(buf),0);
sprintf(buf,"\r
");
send(client,buf,strlen(buf),0);
sprintf(buf,"Error prohibited CGI execution.\r
"
);
send(client,buf,strlen(buf),0);
}
void execute_cgi(int client,const char *path ,const char * method ,const char *query_string)
{
char buf[1024];
int cgi_output[2];//pipe ,
int cgi_input[2];
pid_t pid;
int status;
int i;
char c;
int numchar =1;
int content_length=-1;
buf[0]='A';
buf[1]='\0'
// header
if(strcasecmp(method,"GET")==0)// strcasecmp ,
while((numchar>0)&&strcmp("
"buf))
numchar=get_line(client,buf,sizeof(buf));//
else
{
//
numchar=get_line(client,buf,sizeof(buf));
{
buf[15]='\0';
if(strcasecmp(buf,"content_length:")==0)
content_length=atoi(&(buf[16]));
numchar=get_line(client,buf,sizeof(buf));
}
if(content_length==-1)
{
bad_request(client);
return;//
}
}
sprintf(buf,"HTTP/1.1 200 OK\r
");
send(client,buf,strlen(buf),0);
if(pipe(cgi_output)<0)
{
cannot_execute(client);
return;
}
if(pipe(cgi_input)<0)
{
cannot_execute(client);
return;
}
if((pid=fork())<0)
{
cannot_execute(client);
return ;
}
if(pid==0)
{
char meth_env[255];
char querry_env[255];
char length_env[255];
dup2(cgi_input[0],0);//
dup2(cgi_output[1],1);//
close(cgi_input[1]);
close(cgi_output[0]);
// request_method
sprintf(meth_env,"REQUEST_METHOD=%s",method);
putenv(meth_env);
if(strcasecmp(method="GET")==0)
{
sprintf(querry_env,"QUERRY_STRING=%s",query_string);
putenv(querry_env);
}
else
{
sprintf(length_env,"Content-type=%d",content_length);
putenv(length_env);
}
execl(path,path,NULL);// execl cgi
exit(0);
}
else
{
close(cgi_output[1]);
close(cgi_input[0]);
//
if(strcasecmp(method,"POST")==0)
for(i=0;i1,0);
write(cgi_input[1],&c,1);
}
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);// ,
}
}
int get_line(int sock,cahr*buf,int size)
{
int i=0;
char c='\0';
int n;
// buf
while((i1)&&(c!='
'))
{
n=recv(sock,&c,1,0);
if(n>0)
{
if(c=='\r')
{
n=recv(sock,&c,1,MSG_PEEK);
if((n>0)&&(c=='
'))
recv(sock,&c,1,0);
else
c='
';
}
buf[i]=c;
i++;
}
else
c='
';
}
buf[i]='\0';
return i;
}
void headers(int client ,const char * filename)
{
char buf[1024];
(void)filename;//
strcpy(buf,"HTTP/1.1 200OK\r
");
send(client,buf,strlen(buf),0);
strcpy(buf,SEVER_SIRING);
send(client,buf,strlen(buf),0);
sprintf(buf,"Content-type:text/html\r
");
send(client,buf,strlen(buf),0);
strcpy(buf,"\r
");
send(client,buf,strlen(buf),0);
}
void not_found(int client)
{
// http 404
char buf[1024];
// http
sprintf(buf, "HTTP/1.0 404 NOT FOUND\r
");
send(client, buf, strlen(buf), 0);
// serverName
sprintf(buf, SERVER_STRING);
send(client, buf, strlen(buf), 0);
// Content-Type
sprintf(buf, "Content-Type: text/html\r
");
send(client, buf, strlen(buf), 0);
//
sprintf(buf, "\r
");
send(client, buf, strlen(buf), 0);
// html
sprintf(buf, "Not Found \r
");
send(client, buf, strlen(buf), 0);
sprintf(buf, "The server could not fulfill\r
"
);
send(client, buf, strlen(buf), 0);
sprintf(buf, "your request because the resource specified\r
");
send(client, buf, strlen(buf), 0);
sprintf(buf, "is unavailable or nonexistent.\r
");
send(client, buf, strlen(buf), 0);
sprintf(buf, "\r
");
send(client, buf, strlen(buf), 0);
}
void sever_file(int client,const char * filename)
{
//
FILE *resource = NULL;
int numchars = 1;
char buf[1024];
buf[0] = 'A';
buf[1] = '\0';
while ((numchars > 0) && strcmp("
", buf)) /* read & discard headers */
numchars = get_line(client, buf, sizeof (buf));
resource = fopen(filename, "r");
if (resource == NULL)
not_found(client);
else {
//
headers(client, filename);
cat(client, resource);
}
fclose(resource);
}
void unimplemented(int client)
{
char buf[1024];
sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r
");
send(client, buf, strlen(buf), 0);
sprintf(buf, SERVER_STRING);
send(client, buf, strlen(buf), 0);
sprintf(buf, "Content-Type: text/html\r
");
send(client, buf, strlen(buf), 0);
sprintf(buf, "\r
");
send(client, buf, strlen(buf), 0);
sprintf(buf, "Method Not Implemented\r<br>" );
send(client, buf, strlen(buf), 0);
sprintf(buf, "\r
");
send(client, buf, strlen(buf), 0);
sprintf(buf, "HTTP request method not supported.\r
"
);
send(client, buf, strlen(buf), 0);
sprintf(buf, "\r
");
send(client, buf, strlen(buf), 0);
}
void PrintSocketAddress(const struct sockaddr *address, FILE *stream) {
// Test for address and stream
if (address == NULL || stream == NULL)
return;
void *numericAddress; // Pointer to binary address
// Buffer to contain result (IPv6 sufficient to hold IPv4)
char addrBuffer[INET6_ADDRSTRLEN];
in_port_t port; // Port to print
// Set pointer to address based on address family
switch (address->sa_family) {
case AF_INET:
numericAddress = &((struct sockaddr_in *) address)->sin_addr;
port = ntohs(((struct sockaddr_in *) address)->sin_port);
break;
case AF_INET6:
numericAddress = &((struct sockaddr_in6 *) address)->sin6_addr;
port = ntohs(((struct sockaddr_in6 *) address)->sin6_port);
break;
default:
fputs("[unknown type]", stream); // Unhandled type
return;
}
// Convert binary to printable address
if (inet_ntop(address->sa_family, numericAddress, addrBuffer,
sizeof (addrBuffer)) == NULL)
fputs("[invalid address]", stream); // Unable to convert
else {
fprintf(stream, " %s", addrBuffer);
if (port != 0) // Zero not valid in any socket addr
fprintf(stream, "-%u
", port);
}
}
// ,
//gcc -W -Wall -lsocket -lpthread -o httpd httpd.c linux -lsocket