The Tinyhttpd method and source analysis of the compiler running under Ubuntu

Source: Internet
Author: User
Tags http 200 int size sprintf stdin strcmp server port
Summary

Before reading UNP, I wondered how these socket APIs actually work, and it doesn't seem to have much to do with the daily web. In a debugging flask source code, when I entered the Httpserver and Werkzeug tool Set source code, see a lot of similar UNIX under the Socket API function, so I have a further understanding of UNP. In the application tier, the lowest-level servers are actually developed and run by these APIs, often shouldering requests, distributing requests, and interpreting requests. tinyhttpd

TINYHTTPD should be the simplest of a C-language server open source project, the total length is about 500 lines. It is recommended that UNP beginners (such as I at the beginning of the UNP how to use all do not know) to understand, read the trust will have a deeper understanding of network programming. Compile Run

can go to the official website to download to its source code: TINYHTTPD
After downloading to get a tar.gz end of the compression package, uncompressed can see the following files and directories:

There are no files like config, only one makefile, in the terminal open, direct make can generate executable file. However, there are some areas that need to be modified before make because the author of TINYHTTPD was developed under Solaris and was not suitable for direct compilation under Linux.
The following items need to be changed: the function declaration for line 33 is changed to void accept_request (void); So the following function definitions are modified as well. Change the Accept_request function 77 line to return NULL; Add return NULL at 128 line close (client); The variable type of line 439 and row 484 is changed to socklen_t. 498 lines to if (Pthread_create (&newthread, NULL, Accept_request, (void*) &client_sock)!= 0)

The number of rows is not necessarily accurate, but it is generally nearby.
You can then make the build httpd executable file.
In the terminal input./HTTPD can run the server, and can get the current run port number

In the browser input localhost:45962 will be able to get the page

The page is prompted to say that it is a CGI demo. CGI is meant to be a universal Gateway interface, and is actually a script that generates response content.
For example, the server received a request from the server, it can choose to pass the request to a CGI program, by which to generate content based on the request, the CGI program to return the generated content to the server, the server to return this content to the client. The server program is responsible for processing requests only and is not responsible for processing the content.
It is actually a kind of web development ideas, but still used, but the scope of use is still relatively small. So you will sometimes see the address of certain pages (as I observed the digital tail) that is clearly accessing a PHP or py file, it's not a code, it's a serious page, the content of the Web page is dynamically generated in these PHP files or python files, and then returned to the browser.

so better to play, we can find a pure HTML file, no JS code that kind, or text file, renamed to Index.html, put into the Htdocs folder, then visit, browser display is our custom content ~ HTTP request

To better understand the source code, here is a brief introduction to the knowledge of HTTP requests.
HTTP method to inform server intent
The TINYHTTPD is embodied in the Get and POST method.

Get: Getting Resources
The Get method is used to request access to resources that have been identified by the URI. Returns the response content after the specified resource has been parsed by the server. In other words, if the requested resource is text (a static page request), return it as-is, or, if it is a CGI-like program (a dynamic page request), returns the result of the executed output.

POST: Transport entity Body
The primary purpose of POST is to transfer the body of the entity, not to get the body content of the response

When you use a browser to access a server, the browser initiates an HTTP request to the server. Request a request similar to the one in two diagrams, beginning with the request type (get or post), followed by the requested resource path. Therefore, the request type and the request path can be obtained from the first row of the request.


Introduction to function Functions

Simply explain the role of each function:

Accept_request: Handles an HTTP request that is heard from a socket, where a large part of the server processing request flow can be reflected.
Bad_request: Return to client This is a bad request, HTTP status?
Cat: Reads a file on the server to the socket socket.
Cannot_execute: Primarily handles errors that occur when executing a CGI program.
Error_die: Write the error message to Perror and exit.
EXECUTE_CGI: The process of running a CGI program is also a primary function.
Get_line: Read the socket line, the carriage return line, etc. are unified as line break end.
Headers: Writes the headers of the HTTP response to the socket.
Not_found: The main process when the requested file cannot be found.
Sever_file: Call cat to return the server file to the browser.
Startup: Initialize the HTTPD service, including setting up sockets, binding ports, monitoring, etc.

Recommended source code reading order: main-> startup-> accept_request-> execute_cgi, proficient in the main workflow and then carefully put the source of each function to see.

Work Flow

(1) Server startup, bind httpd service on specified port or randomly selected port.
(2) When an HTTP request is received (in fact, the Listen Port accpet), a thread is derived to run the Accept_request function.
(3) Remove the method (get or POST) and URL from the HTTP request. For a Get method, if there is a carry parameter, the query_string pointer points to the URL. The get parameter that follows.
(4) Format the URL to the path array, representing the server file path requested by the browser, in TINYHTTPD the server file is under the Htdocs folder. When the URL ends with/, or the URL is a directory, the default is to add index.html to the path, which means to access the home page.
(5) If the file path is valid, for no parameter get requests, direct output server files to the browser, that is, in HTTP format to the socket, jump to (10). Other cases (with parameter get,post, URL as executable), call the EXCUTE_CGI function to execute the CGI script.
(6) Read the entire HTTP request and discard it, and find content-length if it is POST. Write the HTTP 200 status code to the socket.
(7) Establish two pipelines, cgi_input and cgi_output, and fork a process.
(8) in the subprocess, redirect the STDOUT to the Cgi_outputt write end, redirect STDIN to the Cgi_input read end, close the cgi_input write end and the read end of the Cgi_output, and set the Request_method Environment variables, get words set query_string environment variables, POST words set CONTENT_LENGTH environment variables, these environment variables are to call the CGI script, and then run the CGI program with EXECL.
(9) In the parent process, turn off the read end of Cgi_input and Cgi_output write end, if post, write post data to Cgi_input, has been redirected to STDIN, read Cgi_output pipeline output to the client, the pipeline input is STDOUT. Then close all the pipes and wait for the child process to end. This part of the mess, see the following figure illustrates:

Figure 1 Pipeline initial state

Figure 2 Pipe Final State
(10) Closes the connection to the browser and completes an HTTP request and response because HTTP is not connected.

Source code Comments

Here is enclosed my own annotated source code, I hope to be helpful to reading.

* J. David ' s webserver * * * * is 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 so
 Laris 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 of the two 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)) #define server_string "server:jdbhttpd/0.1.0\r\n" void *Accept_request (void * tclient);
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 th  E Server port to * return.
 Process the request appropriately. * Parameters:the socket connected to the client * */******************************************************************
 /void *accept_request (void * tclient) {int client = * (int *) tclient;
 Char buf[1024];
 int numchars;
 Char method[255];
 Char url[255];
 Char path[512];
 size_t I, J;
 struct STAT st;      int cgi = 0;

/* becomes true if server decides this is a CGI */char *query_string = NULL; GEt_line is the meaning of reading a line.
 Reads the HTTP request, each line ends with \ r \ n The content in the//BUF is the first line in the HTTP request NumChars = get_line (client, buf, sizeof (BUF)); i = 0;
 j = 0; Isspace checks whether the parameter is a space. That is, read up to 255 characters, until the blanks//in the format of the HTTP request, the content in method is the request type of the HTTP request, GET or post while!
  Isspace (Buf[j]) && (I < sizeof (method)-1)) {Method[i] = buf[j]; i++;
 j + +;
} Method[i] = ';
  STRCASECMP is a comparison of case insensitive (strcasecmp (method, ' get ') && strcasecmp (method, "POST")) {unimplemented (client);
 return NULL;

 } if (strcasecmp (method, "POST") = = 0) cgi = 1;
 i = 0;
 Locate the field where the space begins, followed by the requested resource path while (Isspace (Buf[j]) && (J < sizeof (BUF))) J + +; The URL is the requested resource path while (!
  Isspace (Buf[j]) && (i < sizeof (URL)-1) && (J < sizeof (BUF))) {Url[i] = buf[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 = ' n ';query_string++;
 } sprintf (Path, "htdocs%s", url);
  if (Path[strlen (path)-1] = = '/') strcat (path, "index.html"); Send NotFound if (stat (path, &st) = = 1) {while (NumChars > 0) && strcmp ("\ \") to the client if the resource failed to open locally on the server, buf
  )/* Read & Discard headers */numchars = get_line (client, buf, sizeof (BUF));
 Not_found (client);
 else {//If it is a directory, continue stitching if ((St.st_mode & s_ifmt) = = S_ifdir) strcat (Path, "/index.html");
      If it is an executable file, place the CGI 1 if (St.st_mode & s_ixusr) | |
      (St.st_mode & s_ixgrp) | |
   (St.st_mode & S_ixoth))
  cgi = 1;
  if (!cgi) serve_file (client, path);
 else execute_cgi (client, path, method, query_string);
 Close (client);
return NULL; }/**********************************************************************//* Inform the client that a request it has mad
 E has a problem. * parameters:client socket *//**********************************************************************/void Bad_ Request (int client) {char buf[1024];
 sprintf (buf, "http/1.0 bad 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  Socket. This function * are named after the UNIX "cat" command, because it might have been * easier just to do something like Pip
 E, 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)) {//send is the advanced version of write send (client, buf, strlen (BUF), 0);
 Fgets (buf, sizeof (BUF), Resource);  }/**********************************************************************//* Inform the client that a CGI script could
 Not to be 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 C
 Har *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] = ' the '; if (strcasecmp (method, "get") = = 0) while ((NumChars > 0) && strcmp ("\ n", buf))/* Read & Discard Header
 S */numchars = get_line (client, buf, sizeof (BUF));
  else/* POST */{NumChars = get_line (client, buf, sizeof (BUF)); while (NumChars &Gt
   0) && strcmp ("\ n", buf)) {buf[15] = ';
   if (strcasecmp (buf, "content-length:") = = 0) Content_length = atoi (& (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);
  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);
  Dup2 (Cgi_input[0], 0);
  Close (cgi_output[0]);
  Close (cgi_input[1]);
  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);
  } execl (path, path, NULL);
 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);

  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); }/**********************************************************************//* Get a line from socket, whether the Lin  E ends in a newline, * carriage return, or a CRLF combination.  Terminates the string read * with a null character.  If No newline indicator is found before the ' * End of the buffer, the ' is ' terminated with a null. If any of * The above three line terminators are read, the last character of the * would be a linefeed and the Stri Ng would be terminated witH A * null character.  * Parameters:the Socket Descriptor * The buffer to save the size of the buffer * Returns:the number of bytes stored (excluding null) *//**************************************************************
 /////read one line from sock int get_line (int sock, char *buf, int size) {int i = 0;
 char c = ' I ';

 int n; while ((I < size-1) && (c!= ' \ n ')) {//recv is a function in advanced IO, the flag parameter is 0, the actual effect is equivalent to read n = recv (sock, &c, 1, 0
  ); /* DEBUG printf ("%02x\n", c);
    */if (n > 0) {if (c = = ' \ r ') {//msg_peek means view read, read not discard n = recv (sock, &c, 1, Msg_peek); /* DEBUG printf ("%02x\n", c);
    */if ((n > 0) && (c = = ' \ n ')) recv (sock, &c, 1, 0);
   else C = ' \ n ';
   } Buf[i] = C;
  i++;
 else C = ' \ n ';

 } Buf[i] = ';
return (i); }/**********************************************************************//* Return the informational HTTP headers About a file. */
/*Parameters:the socket to print the headers on * The name of the file * * * */*********************************** 
 ///response headers 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");
The following starts with body send (client, buf, strlen (BUF), 0); }/**********************************************************************//* Give a client a 404 Not Found status Messag
 E. *//**********************************************************************///Cannot find resource 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);
 The following starts with the body sprintf (buf, "

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.