Tinyhttp is the simplest Web server that implements the fundamentals of Web servers. The code is all developed in C language, only more than 500 lines, read the code, you can understand the basic principles of Web server.
Download Connection: http://sourceforge.net/projects/tinyhttpd/
After downloading, you will experience problems compiling. In makefile, the execution of the compilation script is:
Gcc-w-wall-lsocket-lpthread-o httpd HTTPD.C
Where Lsocket is not required, because the author of the development of the Linux version with me, I used Ubuntu Server, do not need this line selection.
-lpthread should be replaced with-pthread, for specific reasons refer here.
There are some caveats, because of the problem with the parameter type.
after I compile, the runtime discovers that the home page is not open. With GDB debugging, the breakpoint is added to the sub-process after the fork, in the GDB mode to be set: Set Follow-fork mode child, so that GDB will debug the sub-process after the fork is executed. Finally found that index.html has execute permission, need to remove this permission to line.
<span style= "FONT-SIZE:14PX;" >/* J David ' s webserver *//* This is a simple webserver. * Created November 1999 by J. David Blackstone. * CSE 4344 (Network Concepts), Prof. Zeigler * University of Texas at Arlington *//* This program compiles for Sparc Solar IS 2.6. * To compile for Linux: * 1) Comment out of the #include <pthread.h> line. * 2) Comment out of the line that defines the variable newthread. * 3) Comment out the lines, that run Pthread_create (). * 4) Uncomment the line that runs Accept_request (). * 5) Remove-lsocket from the Makefile. * * #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))//check if ' x ' is a white-space#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);/**********************************************************************//* A request has caused A call to accept () on the server port to * return. Process the request appropriately. * Parameters:the socket connected to the client *//****************************************************************** /void accept_request (int client) {char buf[1024]; int numchars; char method[255]; char url[255]; char path[512]; size_ t I, J; struct STAT St;//record The information about the file int cgi = 0; /* becomes true if server decides this is a CGI * program */char *query_string = NULL; NumChars = Get_line (client, buf, sizeof (BUF)); i = 0; j = 0; while (! ISspace (Buf[j]) && (I < sizeof (method)-1)//' method ' contains the HTTP method (Get/post) {method[i] = Buf[j] ; i++; j + +; } Method[i] = ' + '; Compares the characters ignoring case of character if (strcasecmp (method, "GET") && strcasecmp (method, "POST")) {unimplemented (client); Return } if (strcasecmp (method, "POST") = = 0) cgi = 1; i = 0; while (ISspace (Buf[j) && (J < sizeof (BUF))//skip the Header j + +; while (! ISspace (Buf[j]) && (i < sizeof (URL)-1) && (J < sizeof (BUF)))//copy the URL to ' URL ' {url[i] = bu F[J]; i++; j + +; } Url[i] = ' + '; if (strcasecmp (method, "GET") = = 0) {query_string = URL; while (*query_string! = '? ') && (*query_string! = ') ") query_string++; if (*query_string = = '? ') {cgi = 1; *query_string = ' + '; query_string++; }} sprintf (Path, "htdocs%s", url),//write ' url ' to path and add ' Htdocs ' at the head of path if (PATh[strlen (Path)-1] = = '/') strcat (path, "index.html"); if (stat (path, &st) = =-1) {//check If the file exist or not ((NumChars > 0) && strcmp ("\ n", buf)) /* Read & Discard headers */numchars = get_line (client, buf, sizeof (BUF)); Not_found (client);//file not exist} Else//file exist {if ((St.st_mode & s_ifmt) = = S_ifdir)//a directory strcat (p Ath, "/index.html"); if ((St.st_mode & s_ixusr) | | Check the permission owner, group user or other users (St.st_mode & s_ixgrp) | | (St.st_mode & S_ixoth)) cgi = 1; if (!cgi)//not a CIG serve_file (client, path); else execute_cgi (client, path, method, query_string); } close (client);} /**********************************************************************//* Inform the client a request it has made Has a problem. * Parameters:client Socket *//**********************************************************************/void Bad_ Request (int client) {char buf[1024]; sprintf (buf, "http/1.0 request\r\n "); Send (client, buf, sizeof (BUF), 0); sprintf (buf, "content-type:text/html\r\n"); Send (client, buf, sizeof (BUF), 0); sprintf (buf, "\ r \ n"); Send (client, buf, sizeof (BUF), 0); sprintf (buf, "<p>your browser sent a bad request,"); Send (client, buf, sizeof (BUF), 0); sprintf (BUF, "such as a POST without a content-length.\r\n"); Send (client, buf, sizeof (BUF), 0);} /**********************************************************************//* Put The entire contents of a file out on a Soc Ket. This function * was named after the UNIX "cat" command, because it might has been * easier just to does something like pipe, Fork, and exec ("cat"). * Parameters:the Client Socket Descriptor * File pointer for the file to cat *//***************************** /void Cat (int client, FILE *resource) {char buf[1024]; fgets (buf, sizeof (BUF) , resource); while (!feof (Resource))//feof:check End-of-file Indicator {Send (client, buf, strlen (BUF), 0); Fgets (buf, sizeof (BUF), Resource); }}/**********************************************************************//* Inform the client that a CGI script could Not being executed. * Parameter:the Client Socket descriptor. *//**********************************************************************/void cannot_execute (int client) {Char BUF[1024]; sprintf (buf, "http/1.0 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);} /**********************************************************************//* Print out a error message with perror () ( for system errors; Based * On value of errno, which indicates system call errors) and exit the * program indicating an error. *//**********************************************************************/vOID Error_die (const char *SC) {perror (SC); exit (1);} /**********************************************************************//* Execute a CGI script. would need to set environment variables as * appropriate. * Parameters:client Socket Descriptor * Path to the CGI script *//******************************************* /void execute_cgi (int client, const char *path, const char *method, const char *query_string) {char buf[1024]; int cgi_output[2];//pipe to communicate between parent and child int cgi_input[2]; pid_t p Id int status; int i; char c; int numchars = 1; int content_length =-1; Buf[0] = ' A '; Buf[1] = ' + '; if (strcasecmp (method, "GET") = = 0) while ((NumChars > 0) && strcmp ("\ n", buf)/*/Read & Discard Headers */NumChars = get_line (client, buf, sizeof (BUF)); else/* POST */{NumChars = get_line (client, buf, sizeof (BUF)); while ((NumChars > 0) && strcmp ("\ n", buf)) {buf[15] = ' + ';//seT buf[15]= ' to compare with "Content-length:" If (strcasecmp (buf, "content-length:") = = 0) content_length = atoi (& amp; (buf[16])); NumChars = get_line (client, buf, sizeof (BUF)); } if (content_length = =-1) {bad_request (client); Return }} sprintf (buf, "http/1.0 ok\r\n"); Send (client, buf, strlen (BUF), 0),//send respond status 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)/* child:cgi script */{char meth_env[255]; Char query_env[255]; Char length_env[255]; Dup2 (Cgi_output[1], 1);//redirect cgi_output[1] to standard output dup2 (Cgi_input[0], 0);//redirect Cgi_input[0] to Stan Dard input Close (cgi_output[0])//the parent process use cgi_output[0] to input data close (cgi_input[1]);//the parent PR Ocess use cig_input[1] to output data 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 {/* POST */sprintf (length_env, "content_length=%d", content_length); Putenv (length_env);//and value of environment variables} execl (path, path, NULL);//execute the CGI program exit (0); } else {/* parent */close (cgi_output[1]); Close (cgi_input[0]); if (strcasecmp (method, "POST") = = 0) for (i = 0; i < content_length; i++) {recv (client, &c, 1, 0); Write (cgi_input[1], &c, 1),//pass the para to child process} while (read (cgi_output[0], &c, 1) > 0)//read t He child process result Send (client, &c, 1, 0); Close (cgi_output[0]); Close (cgi_input[1]); Waitpid (PID, &status, 0);//wait for child process until it ends}}/************************************************ * Get a line from a socket, whether the line ends in a newline, * carriage return, or a CRLF combi Nation. Terminates the String Read * with a null character. If No newline indicator is found before the * end of the buffer, the string was terminated with a null. If any of the above three line terminators are read, the last character of the * string would be a linefeed and the string 'll is terminated with a * null character. * Parameters:the Socket Descriptor * The buffer to save the data in * the size of the buffer * Re Turns:the number of bytes stored (excluding null) *//**************************************************************** /int get_line (int sock, char *buf, int size) {int i = 0; char c = ' n '; int n; while ((I < size-1) && ( c! = ' \ n ')) {n = recv (sock, &c, 1, 0); /* DEBUG printf ("%02x\n", c); */if (n > 0) {if (c = = ' \ r ') {n = recv (sock, &c, 1, Msg_peek);//peek, in fact don ' t read the data. /* DEBUG printf ("%02x\n", c); */if ((n > 0) && (c = = ' \ n '))//if terminated with CRLF recv (sock, &c, 1, 0); else C = ' \ n '; } Buf[i] = C; i++; } else C = ' \ n '; } Buf[i] = ' + ';//the buffer end with null return (i);} /**********************************************************************//* Return the informational HTTP headers About a file. *//* parameters:the socket To print the headers on * The name of the file *//******************************** /void Headers (int client, const char *filename) {char buf[1024]; (void) filename; /* could use filename to determine file type */strcpy (buf, "http/1.0 ok\r\n"); Send (client, buf, strlen (BUF), 0); strcpy (buf, server_string); Send (client, buf, strlen (BUF), 0); sprintf (buf, "content-type:text/html\r\n"); Send (client, buf, strlen (BUF), 0); strcpy (buf, "\ r \ n"); Send (client, buf, strlen (BUF), 0);} /**********************************************************************//* Give a client a 404 Not Found status message . *//******************************************************************/void not_found (int client) {char buf[1024]; sprintf (buf, "http/1.0 404 Not Found\r\n"); Send (client, buf, strlen (BUF) , 0); sprintf (buf, server_string); 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, "
Tinyhttp Source Code Interpretation