有关于TinyHTTPd的源码解析网站已经很多,本文仅记录学习
运行环境CentOS 8,QT;代码可以运行但是有bug,但是用于理解阅读还算可以
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <ctype.h>
#include <strings.h>
#include <string.h>
#include <sys/stat.h>
#include <pthread.h>
#include <sys/wait.h>
#include <stdlib.h>
#define ISspace(x) isspace((int)(x))
#define SERVER_STRING "Server: jdbhttpd/0.1.0\r\n"
void accept_request(int);
void bad_request(int);
void cat(int, FILE *);
void cannot_execute(int);
void error_die(const char *);
void execute_cgi(int, const char *, const char *, const char *);
int get_line(int, char *, int);
void headers(int, const char *);
void not_found(int);
void serve_file(int, const char *);
int startup(u_short *);
void unimplemented(int);
int main(void)
{
printf("run 1");
int server_sock=-1;
int client_sock=-1;
u_short port=0;
struct sockaddr_in client_name;
pthread_t newthread;
printf("run 1");
server_sock=startup(&port);
printf("server_sock running on port : %d \n",port);
int client_name_len=sizeof(client_name);
while(1){
client_sock = accept(server_sock,(struct sockaddr*)&client_name,&client_name_len);
if(client_sock==-1){
error_die("accept");
}
if(pthread_create(&newthread,NULL,accept_request,client_sock)){
perror("pthread_create");
}
}
close(server_sock);
return 0;
}
int startup(u_short * port)
{
int server_sock=0;
struct sockaddr_in server_name;
server_sock=socket(PF_INET,SOCK_STREAM,0);
if(server_sock==-1){
error_die("socket");
}
memset(&server_name,0,sizeof(server_name));
server_name.sin_family=AF_INET;
server_name.sin_port=htons(*port);
server_name.sin_addr.s_addr=htonl(INADDR_ANY);
if(bind(server_sock,(struct sockaddr*)& server_name,sizeof(server_name))<0){
error_die("bind");
}
if(*port == 0){
int server_name_length=sizeof(server_name);
if(getsockname(server_sock,(struct sockaddr*)&server_name, &server_name_length)== -1){
error_die("getsockname");
}
*port=ntohs(server_name.sin_port);
}
if(listen(server_sock,5)<0){
error_die("listen");
}
return server_sock;
}
void error_die(const char * error)
{
perror(error);
exit(1);
}
void accept_request(int client_sock)
{
char buf[1024];
int numchars;
char method[255];
char url[255];
char path[512];
size_t i,j;
struct stat st;
int cgi=0;
char *query_string=NULL;
numchars=get_line(client_sock,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_sock);
return;
}
if(strcasecmp(method,"POST") == 0){
cgi=1;
}
i=0;
while(ISspace(buf[j]) && j<sizeof(buf)){
j++;
}
while(!ISspace(buf[j]) && i<sizeof(url)-1 && j<sizeof(buf)){
url[i]=buf[j];
i++;
j++;
}
url[i]='\0';
if(strcasecmp(method,"GET") == 0){
query_string = url;
while((*query_string)!='?' && (*query_string)!= '\0'){
query_string++;
}
if(*query_string == '?'){
cgi=1;
*query_string='\0';
query_string++;
}
}
sprintf(path,"htdocs%s",url);
if(path[strlen(path)-1] == '/'){
strcat(path,"index.html");
}
if(stat(path,&st)==-1){
while(numchars>0 && strcmp("\n",buf)){
numchars=get_line(client_sock,buf,sizeof(buf));
not_found(client_sock);
}
}
else{
if((st.st_mode & S_IFMT) == S_IFDIR){
strcat(path,"/index.html");
}
if((st.st_mode & S_IXUSR)||
(st.st_mode & S_IXGRP)||
(st.st_mode & S_IXOTH)){
cgi=1;
}
if(!cgi){
serve_file(client_sock,path);
}
else{
execute_cgi(client_sock,path,method,query_string);
}
}
close(client_sock);
}
void serve_file(int client_sock, const char *path)
{
FILE *resource=NULL;
int numchars=1;
char buf[1024];
buf[0]='A';
buf[1]='\0';
while(numchars>0 && strcmp("\n",buf)){
numchars=get_line(client_sock,buf,sizeof (buf));
}
resource = fopen(path,"r");
if(resource == NULL){
not_found(client_sock);
}
else{
headers(client_sock,path);
cat(client_sock,resource);
}
fclose(resource);
}
void headers(int client_sock, const char *path)
{
char buf[1024];
(void)path;
strcpy(buf, "HTTP/1.0 200 OK\r\n");
send(client_sock, buf, strlen(buf), 0);
strcpy(buf, SERVER_STRING);
send(client_sock, buf, strlen(buf), 0);
sprintf(buf, "Content-Type: text/html\r\n");
send(client_sock, buf, strlen(buf), 0);
strcpy(buf, "\r\n");
send(client_sock, buf, strlen(buf), 0);
}
void cat(int client_sock, FILE *resource)
{
char buf[1024];
fgets(buf,sizeof (buf),resource);
while(!feof(resource)){
send(client_sock,buf,strlen(buf),0);
fgets(buf,sizeof (buf),resource);
}
}
int get_line(int client_sock, char *buf, int buf_size)
{
int i=0;
int n;
char c='\0';
while(i<buf_size-1 && c!='\n'){
n=recv(client_sock,&c,1,0);
if(n>0){
if(c=='\r'){
n=recv(client_sock,&c,1,MSG_PEEK);
if(n>0 && c=='\n'){
recv(client_sock,&c,1,0);
}
else{
c='\n';
}
}
buf[i]=c;
i++;
}
else{
c='\n';
}
}
buf[i]='\0';
return i;
}
void unimplemented(int client_sock)
{
char buf[1024];
sprintf(buf,"HTTP/1.0 501 Method Not Implemented\r\n");
send(client_sock,buf,strlen(buf),0);
sprintf(buf,SERVER_STRING);
send(client_sock,buf,strlen(buf),0);
sprintf(buf,"Content-Type:text/html\r\n");
send(client_sock,buf,strlen(buf),0);
sprintf(buf,"\r\n");
send(client_sock,buf,strlen(buf),0);
sprintf(buf,"<HTML><HEAD><TITLE>Method Not Implemented\r\n");
send(client_sock,buf,strlen(buf),0);
sprintf(buf,"</TITLE></HEAD>\r\n");
send(client_sock,buf,strlen(buf),0);
sprintf(buf,"<BODY><P>HTTP request method not supported.\r\n");
send(client_sock,buf,strlen(buf),0);
sprintf(buf,"</BODY></HTML>\r\n");
send(client_sock,buf,strlen(buf),0);
}
void not_found(int client_sock)
{
char buf[1024];
sprintf(buf, "HTTP/1.0 404 NOT FOUND\r\n");
send(client_sock, buf, strlen(buf), 0);
sprintf(buf, SERVER_STRING);
send(client_sock, buf, strlen(buf), 0);
sprintf(buf, "Content-Type: text/html\r\n");
send(client_sock, buf, strlen(buf), 0);
sprintf(buf, "\r\n");
send(client_sock, buf, strlen(buf), 0);
sprintf(buf, "<HTML><TITLE>Not Found</TITLE>\r\n");
send(client_sock, buf, strlen(buf), 0);
sprintf(buf, "<BODY><P>The server could not fulfill\r\n");
send(client_sock, buf, strlen(buf), 0);
sprintf(buf, "your request because the resource specified\r\n");
send(client_sock, buf, strlen(buf), 0);
sprintf(buf, "is unavailable or nonexistent.\r\n");
send(client_sock, buf, strlen(buf), 0);
sprintf(buf, "</BODY></HTML>\r\n");
send(client_sock, buf, strlen(buf), 0);
}
void execute_cgi(int client_sock, const char *path, const char *method, const char *query_string)
{
char buf[1024];
int cgi_output[2];
int cgi_input[2];
pid_t pid;
int status;
int i;
char c;
int numchars=1;
int content_length=-1;
buf[0]='A';
buf[1]='\0';
if(strcasecmp(method,"GET")==0){
while(numchars>0 && strcmp("\n",buf)){
numchars=get_line(client_sock,buf,sizeof(buf));
}
}
else{
numchars = get_line(client_sock,buf,sizeof (buf));
while(numchars>0 && strcmp("\n",buf)){
buf[15]='\0';
if(strcasecmp(buf,"Content-Length:")==0){
content_length = atoi(&(buf[16]));
}
numchars = get_line(client_sock,buf,sizeof (buf));
}
if(content_length==-1){
bad_request(client_sock);
return;
}
}
sprintf(buf,"HTTP/1.0 200 OK\r\n");
send(client_sock,buf,strlen(buf),0);
if(pipe(cgi_output)<0){
cannot_execute(client_sock);
return;
}
if(pipe(cgi_input)<0){
cannot_execute(client_sock);
return;
}
if((pid=fork())<0){
cannot_execute(client_sock);
return;
}
if(pid == 0){
char meth_env[255];
char query_env[255];
char length_env[255];
dup2(cgi_output[1],1);
dup2(cgi_input[0],0);
close(cgi_input[1]);
close(cgi_output[0]);
sprintf(meth_env,"REQUEST_METHOD=%s",method);
putenv(meth_env);
if(strcasecmp(method,"GET")==0){
sprintf(query_env,"QUERY_STRING=%s",query_string);
putenv(query_env);
}
else{
sprintf(length_env,"CONTENT_LENGTH=%d",content_length);
putenv(length_env);
}
execl(path,path,NULL);
exit(0);
}
else{
close(cgi_output[1]);
close(cgi_input[0]);
if(strcasecmp(method,"POST")==0){
for(i=0;i<content_length;i++){
recv(client_sock,&c,1,0);
write(cgi_input[1],&c,1);
}
while(read(cgi_output[0],&c,1)>0){
send(client_sock,&c,1,0);
}
close(cgi_output[0]);
close(cgi_input[1]);
waitpid(pid,&status,0);
}
}
}
void bad_request(int client_sock)
{
char buf[1024];
sprintf(buf, "HTTP/1.0 400 BAD REQUEST\r\n");
send(client_sock, buf, sizeof(buf), 0);
sprintf(buf, "Content-type: text/html\r\n");
send(client_sock, buf, sizeof(buf), 0);
sprintf(buf, "\r\n");
send(client_sock, buf, sizeof(buf), 0);
sprintf(buf, "<P>Your browser sent a bad request, ");
send(client_sock, buf, sizeof(buf), 0);
sprintf(buf, "such as a POST without a Content-Length.\r\n");
send(client_sock, buf, sizeof(buf), 0);
}
void cannot_execute(int client)
{
char buf[1024];
sprintf(buf, "HTTP/1.0 500 Internal Server Error\r\n");
send(client, buf, strlen(buf), 0);
sprintf(buf, "Content-type: text/html\r\n");
send(client, buf, strlen(buf), 0);
sprintf(buf, "\r\n");
send(client, buf, strlen(buf), 0);
sprintf(buf, "<P>Error prohibited CGI execution.\r\n");
send(client, buf, strlen(buf), 0);
}