Simple Linux Web Server (C Language)
I. Project Introduction
The web server provides static page (HTML) resources, the transport layer is based on the TCP protocol, and the application layer is based on the HTTP protocol.
The browser sends an HTTP request, and the server parses the request after receiving it; parses out the resource path requested by the browser, verifies whether the resource is legal and confirms its type, obtains the resource content locally, organizes it into an HTTP response and sends it back to the web server. The web server supports concurrent processing (multi-threading).
Makefile can provide quick compilation and generation of executable files.
# Quickly compile and generate .o files and executable files
$ make
# Quickly remove all of the .o files and executable files
$ make clean
II. Project Structure
-
Module Introduction
a. HTTP Module ( http.h, http.c)
-
Functionality
Parse HTTP requests:
After the HTTP request sent from the client is received by the server, important data to be used later must be extracted from the request and stored.
Construct HTTP respond:
After receiving the client’s HTTP request, the server sends a response back to the client. The response is divided into two parts: the response header and the response body. The response header is completed by this module.
- Function and Variable:
// HTTP request typedef struct tag_HttpRequest {...} HTTP_REQUEST; // HTTP respond typedef struct tag_HttpRespond {...} HTTP_RESPOND; // Parse request int parseRequest(char const* req, HTTP_REQUEST* hreq); // Construct repond head void constructHead(HTTP_RESPOND const* hres, char* head);
b. Resource Module (resource.h, resource.c, mime.h)
-
Functionality:
Determine whether the file exists and the file type. If the file can not be found in server, then server send the 404 respond to client.
-
Function and Variable:
// Filename Suffix: Content-Type Mapping Table static struct{...} s_minme[] = {...}; // Search resource int searchResource(char const* path); // Identify file types int identifyType(char const* path, char* type);
c. Socket Module (socket.h, socket.c)
-
Functionality
Initializing the socket: The server transport layer uses the TCP protocol. When setting up the server, it needs to create sockets, organize address structures, bind, listen and other functions.
Waiting for and receiving the client’s connection request: When the client needs to access the server, it needs to complete a three-way handshake. The server needs to wait for the arrival of the connection request, complete the three-way handshake, and establish a communication connection with the client.
Receive the client’s HTTP request: After the server and the client establish a communication connection, the client sends an HTTP request to the server. The server needs to receive the request and complete the storage for subsequent operations.
Send response header: The response header is constructed by the HTTP module. In this module, it is responsible for sending the constructed response header to the client.
Sending the response body: The response body is the specific file content requested by the client. The server needs to obtain the file locally, read the content of the file, and send it to the client.
Close the socket.
-
Function and Variable:
// Initialze socket int initSocket(short port); // Accept client connection int acceptClient(void); // Receive request char* recvRequest(int conn); // Send respond head int sendHead(int conn, char const* head); // Send respond Body int sendBody(int conn, char const* path); // Terminate socket void terminateSocket(void);
d. Thread Module (client.h, client.c)
-
Functionality
This module is actually a process function of threads, and each thread is responsible for communicating with a client.
Receive requests from clients, parse the content of the requests, and obtain key information such as resource paths. Determine the resource requested by the client to see if it exists locally. If it does not exist, respond to the client with a 404.html file and determine the file type requested by the client. Construct the response header and complete the return of the response header and response body.
-
Function and variable:
// Client parameter typedef struct tag_ClientArgs{...} CA; // Client thread process void* client(void* arg);
e. Signals Module (signals.h, signals.c)
-
Functionality
In this module, most signals should be ignored (not blocked) to improve the server’s working ability and efficiency. Signals that are not ignored: SIGINT(2) and SIGTERM(15) are used for control, SIGKILL(9) and SIGSTOP(19) cannot be ignored, and signals 32 and 33 do not exist.
-
Function and varaiable:
// Initialze signals int initSignals(void);
f. Server Module (server.h, server.c)
-
Functionality
This module is mainly responsible for receiving connection requests from clients and creating threads.
Initialize the server: mainly responsible for completing the initialization of the server, including creating sockets, ignoring most signals, etc.
Running server: It is mainly responsible for cyclically receiving connection requests from clients and establishing communication connections, obtaining communication sockets, creating threads, passing communication sockets into threads, and completing communication with clients.
Terminate the server.
-
Function and variable:
// Initialize the maximum number of file descriptors static int initMaxFiles(void); // Initialize the server int initServer(short port); // Run server int runServer(char const* home); // Terminate server void terminateServer(void);
g. Main Module (main.c)
-
Functionality
This module is the last module implemented in the project, that is, the main function. In the main function, the functions of the server module are called to complete the initialization and running functions of the server. In this module, you can specify the port and resource path bound to the server based on the content passed by the command line parameters. If the command line arguments are missing, the default port number and home directory will be used.
-
Function and variable:
// main int main(int argc, char* argc[]);
III. Test
-
IV. Project Code
-
HTTP Module
-
http.h
#ifndef _HTTP_H #define _HTTP_H #include <limits.h> #include <sys/types.h> // HTTP request typedef struct tag_HttpRequest{ char method[32]; // Method: GET, POST... char path[PATH_MAX+1]; // Path of the file or directory char protocol[32]; // Protocol: HTTP, HTTPS... char connection[32]; // Connection mode: keep-alive, close } HTTP_REQUEST; // HTTP respond typedef struct tag_HttpRespond{ char protocol[32]; // Protocol: HTTP, HTTPS... int status; // Status: 404, 200... char description[256]; // Describe the status: 404 ---> Not Found char type[32]; // Resource type off_t length; // off_t: signed long char connection[32]; // Connection mode } HTTP_RESPOND; // Parse request int parseRequest(char const* req, HTTP_REQUEST* hreq); // Construct repond head void constructHead(HTTP_RESPOND const* hres, char* head); #endif
-
http.c
#include <stdio.h> #define __USE_GNU #include <string.h> #include <time.h> #include <unistd.h> #include <sys/syscall.h> #include "http.h" // Parse request int parseRequest(char const* req, HTTP_REQUEST* hreq){ /* request looks like below GET /xxx/xxx HTTP/1.1\r\n Host: localhost:8000\r\n User-Agent: Mozilla/5.0\r\n Accept: text/html\r\n Connection: keep-alive\r\n \r\n ... */ sscanf(req, "%s%s%s", hreq->method, hreq->path, hreq->protocol); // "GET /xxx/xxx HTTP\r\n" ---> method = GET, path = /xxx/xxx, protocol = HTTP char* connection = strcasestr(req, "connection"); // Find the 'connection' in the request and does not care the case if (connection != NULL){ sscanf(connection, "%*s%s", hreq->connection); // Connection: keep-alive ---> do not care the 'Connection:', hreq->connection = keep-alive } printf("Process:%d. Thread:%ld>>>>>> [%s][%s][%s][%s]\n", getpid(), syscall(SYS_gettid), hreq->method, hreq->path, hreq->protocol, hreq->connection); // syscall to get the realthread id if (strcasecmp(hreq->method, "GET")){ // This server only support the GET method printf("Process:%d. Thread:%ld>>>>>> Invaild method.\n", getpid(), syscall(SYS_gettid)); return -1; } if (strcasecmp(hreq->protocol, "HTTP/1.0") && strcasecmp(hreq->protocol, "HTTP/1.1")){ // This server suport HTTP/1.0 && HTTP/1.1 printf("Process:%d. Thread:%ld>>>>>> Invaild protocol.\n", getpid(), syscall(SYS_gettid)); return -1; } return 0; } // Construct respond head void constructHead(HTTP_RESPOND const* hres, char* head){ /* HTTP/1.1 200 OK\r\n Sever: Webserver 1.0\r\n Date: Mon, 4 Sep 2023 00:00:00 GMT\r\n Content-Type: text/html\r\n Connection: keep-alive\r\n \r\n ... */ char dateTime[32]; time_t now = time(NULL); strftime(dateTime, sizeof(dateTime), "%a, %d %b %Y %T GMT", gmtime(&now)); // Format time %a ----> Mon sprintf(head, "%s %d %s\r\n" "Server: Eason Webserver 1.0\r\n" "Date: %s\r\n" "Content-Type: %s\r\n" "Content-Length: %ld\r\n" "Connection: %s\r\n\r\n", hres->protocol, hres->status, hres->description, dateTime, hres->type, hres->length, hres->connection); }
-
-
Resource Module
-
mime.h
#ifndef _MIME_H #define _MIME_H static struct { char suffix[256]; char type[256]; } s_mime[] = { {"." , "application/x-" }, {".*" , "application/octet-stream" }, {".001" , "application/x-001" }, {".301" , "application/x-301" }, {".323" , "text/h323" }, {".906" , "application/x-906" }, {".907" , "drawing/907" }, {".acp" , "audio/x-mei-aac" }, {".ai" , "application/postscript" }, {".aif" , "audio/aiff" }, {".aifc" , "audio/aiff" }, {".aiff" , "audio/aiff" }, {".a11" , "application/x-a11" }, {".anv" , "application/x-anv" }, {".apk" , "application/vnd.android.package-archive"}, {".asa" , "text/asa" }, {".asf" , "video/x-ms-asf" }, {".asp" , "text/asp" }, {".asx" , "video/x-ms-asf" }, {".au" , "audio/basic" }, {".avi" , "video/avi" }, {".awf" , "application/vnd.adobe.workflow" }, {".biz" , "text/xml" }, {".bmp" , "application/x-bmp" }, {".bot" , "application/x-bot" }, {".c4t" , "application/x-c4t" }, {".c90" , "application/x-c90" }, {".cal" , "application/x-cals" }, {".cat" , "application/vnd.ms-pki.seccat" }, {".cdf" , "application/x-netcdf" }, {".cdr" , "application/x-cdr" }, {".cel" , "application/x-cel" }, {".cer" , "application/x-x509-ca-cert" }, {".cg4" , "application/x-g4" }, {".cgm" , "application/x-cgm" }, {".cit" , "application/x-cit" }, {".class" , "java/*" }, {".cml" , "text/xml" }, {".cmp" , "application/x-cmp" }, {".cmx" , "application/x-cmx" }, {".cot" , "application/x-cot" }, {".crl" , "application/pkix-crl" }, {".crt" , "application/x-x509-ca-cert" }, {".csi" , "application/x-csi" }, {".css" , "text/css" }, {".cut" , "application/x-cut" }, {".dbf" , "application/x-dbf" }, {".dbm" , "application/x-dbm" }, {".dbx" , "application/x-dbx" }, {".dcd" , "text/xml" }, {".dcx" , "application/x-dcx" }, {".der" , "application/x-x509-ca-cert" }, {".dgn" , "application/x-dgn" }, {".dib" , "application/x-dib" }, {".dll" , "application/x-msdownload" }, {".doc" , "application/msword" }, {".dot" , "application/msword" }, {".drw" , "application/x-drw" }, {".dtd" , "text/xml" }, {".dwf" , "application/x-dwf" }, {".dwg" , "application/x-dwg" }, {".dxb" , "application/x-dxb" }, {".dxf" , "application/x-dxf" }, {".edn" , "application/vnd.adobe.edn" }, {".emf" , "application/x-emf" }, {".eml" , "message/rfc822" }, {".ent" , "text/xml" }, {".epi" , "application/x-epi" }, {".eps" , "application/x-ps" }, {".eps" , "application/postscript" }, {".etd" , "application/x-ebx" }, {".exe" , "application/x-msdownload" }, {".fax" , "image/fax" }, {".fdf" , "application/vnd.fdf" }, {".fif" , "application/fractals" }, {".fo" , "text/xml" }, {".frm" , "application/x-frm" }, {".g4" , "application/x-g4" }, {".gbr" , "application/x-gbr" }, {".gif" , "image/gif" }, {".gl2" , "application/x-gl2" }, {".gp4" , "application/x-gp4" }, {".hgl" , "application/x-hgl" }, {".hmr" , "application/x-hmr" }, {".hpg" , "application/x-hpgl" }, {".hpl" , "application/x-hpl" }, {".hqx" , "application/mac-binhex40" }, {".hrf" , "application/x-hrf" }, {".hta" , "application/hta" }, {".htc" , "text/x-component" }, {".htm" , "text/html" }, {".html" , "text/html" }, {".htt" , "text/webviewhtml" }, {".htx" , "text/html" }, {".icb" , "application/x-icb" }, {".ico" , "image/x-icon" }, {".iff" , "application/x-iff" }, {".ig4" , "application/x-g4" }, {".igs" , "application/x-igs" }, {".iii" , "application/x-iphone" }, {".img" , "application/x-img" }, {".ins" , "application/x-internet-signup" }, {".ipa" , "application/vnd.iphone" }, {".isp" , "application/x-internet-signup" }, {".IVF" , "video/x-ivf" }, {".java" , "java/*" }, {".jfif" , "image/jpeg" }, {".jpe" , "image/jpeg" }, {".jpeg" , "image/jpeg" }, {".jpg" , "image/jpeg" }, {".js" , "application/x-javascript" }, {".jsp" , "text/html" }, {".la1" , "audio/x-liquid-file" }, {".lar" , "application/x-laplayer-reg" }, {".latex" , "application/x-latex" }, {".lavs" , "audio/x-liquid-secure" }, {".lbm" , "application/x-lbm" }, {".lmsff" , "audio/x-la-lms" }, {".ls" , "application/x-javascript" }, {".ltr" , "application/x-ltr" }, {".m1v" , "video/x-mpeg" }, {".m2v" , "video/x-mpeg" }, {".m3u" , "audio/mpegurl" }, {".m4e" , "video/mpeg4" }, {".mac" , "application/x-mac" }, {".man" , "application/x-troff-man" }, {".math" , "text/xml" }, {".mdb" , "application/msaccess" }, {".mfp" , "application/x-shockwave-flash" }, {".mht" , "message/rfc822" }, {".mhtml" , "message/rfc822" }, {".mi" , "application/x-mi" }, {".mid" , "audio/mid" }, {".midi" , "audio/mid" }, {".mil" , "application/x-mil" }, {".mml" , "text/xml" }, {".mnd" , "audio/x-musicnet-download" }, {".mns" , "audio/x-musicnet-stream" }, {".mocha" , "application/x-javascript" }, {".movie" , "video/x-sgi-movie" }, {".mp1" , "audio/mp1" }, {".mp2" , "audio/mp2" }, {".mp2v" , "video/mpeg" }, {".mp3" , "audio/mp3" }, {".mp4" , "video/mpeg4" }, {".mpa" , "video/x-mpg" }, {".mpd" , "application/vnd.ms-project" }, {".mpe" , "video/x-mpeg" }, {".mpeg" , "video/mpg" }, {".mpg" , "video/mpg" }, {".mpga" , "audio/rn-mpeg" }, {".mpp" , "application/vnd.ms-project" }, {".mps" , "video/x-mpeg" }, {".mpt" , "application/vnd.ms-project" }, {".mpv" , "video/mpg" }, {".mpv2" , "video/mpeg" }, {".mpw" , "application/vnd.ms-project" }, {".mpx" , "application/vnd.ms-project" }, {".mtx" , "text/xml" }, {".mxp" , "application/x-mmxp" }, {".net" , "image/pnetvue" }, {".nrf" , "application/x-nrf" }, {".nws" , "message/rfc822" }, {".odc" , "text/x-ms-odc" }, {".out" , "application/x-out" }, {".p10" , "application/pkcs10" }, {".p12" , "application/x-pkcs12" }, {".p7b" , "application/x-pkcs7-certificates" }, {".p7c" , "application/pkcs7-mime" }, {".p7m" , "application/pkcs7-mime" }, {".p7r" , "application/x-pkcs7-certreqresp" }, {".p7s" , "application/pkcs7-signature" }, {".pc5" , "application/x-pc5" }, {".pci" , "application/x-pci" }, {".pcl" , "application/x-pcl" }, {".pcx" , "application/x-pcx" }, {".pdf" , "application/pdf" }, {".pdx" , "application/vnd.adobe.pdx" }, {".pfx" , "application/x-pkcs12" }, {".pgl" , "application/x-pgl" }, {".pic" , "application/x-pic" }, {".pko" , "application/vnd.ms-pki.pko" }, {".pl" , "application/x-perl" }, {".plg" , "text/html" }, {".pls" , "audio/scpls" }, {".plt" , "application/x-plt" }, {".png" , "image/png" }, {".pot" , "application/vnd.ms-powerpoint" }, {".ppa" , "application/vnd.ms-powerpoint" }, {".ppm" , "application/x-ppm" }, {".pps" , "application/vnd.ms-powerpoint" }, {".ppt" , "application/vnd.ms-powerpoint" }, {".ppt" , "application/x-ppt" }, {".pr" , "application/x-pr" }, {".prf" , "application/pics-rules" }, {".prn" , "application/x-prn" }, {".prt" , "application/x-prt" }, {".ps" , "application/x-ps" }, {".ps" , "application/postscript" }, {".ptn" , "application/x-ptn" }, {".pwz" , "application/vnd.ms-powerpoint" }, {".r3t" , "text/vnd.rn-realtext3d" }, {".ra" , "audio/vnd.rn-realaudio" }, {".ram" , "audio/x-pn-realaudio" }, {".ras" , "application/x-ras" }, {".rat" , "application/rat-file" }, {".rdf" , "text/xml" }, {".rec" , "application/vnd.rn-recording" }, {".red" , "application/x-red" }, {".rgb" , "application/x-rgb" }, {".rjs" , "application/vnd.rn-realsystem-rjs" }, {".rjt" , "application/vnd.rn-realsystem-rjt" }, {".rlc" , "application/x-rlc" }, {".rle" , "application/x-rle" }, {".rm" , "application/vnd.rn-realmedia" }, {".rmf" , "application/vnd.adobe.rmf" }, {".rmi" , "audio/mid" }, {".rmj" , "application/vnd.rn-realsystem-rmj" }, {".rmm" , "audio/x-pn-realaudio" }, {".rmp" , "application/vnd.rn-rn_music_package" }, {".rms" , "application/vnd.rn-realmedia-secure" }, {".rmvb" , "application/vnd.rn-realmedia-vbr" }, {".rmx" , "application/vnd.rn-realsystem-rmx" }, {".rnx" , "application/vnd.rn-realplayer" }, {".rp" , "image/vnd.rn-realpix" }, {".rpm" , "audio/x-pn-realaudio-plugin" }, {".rsml" , "application/vnd.rn-rsml" }, {".rt" , "text/vnd.rn-realtext" }, {".rtf" , "application/msword" }, {".rtf" , "application/x-rtf" }, {".rv" , "video/vnd.rn-realvideo" }, {".sam" , "application/x-sam" }, {".sat" , "application/x-sat" }, {".sdp" , "application/sdp" }, {".sdw" , "application/x-sdw" }, {".sis" , "application/vnd.symbian.install" }, {".sisx" , "application/vnd.symbian.install" }, {".sit" , "application/x-stuffit" }, {".slb" , "application/x-slb" }, {".sld" , "application/x-sld" }, {".slk" , "drawing/x-slk" }, {".smi" , "application/smil" }, {".smil" , "application/smil" }, {".smk" , "application/x-smk" }, {".snd" , "audio/basic" }, {".sol" , "text/plain" }, {".sor" , "text/plain" }, {".spc" , "application/x-pkcs7-certificates" }, {".spl" , "application/futuresplash" }, {".spp" , "text/xml" }, {".ssm" , "application/streamingmedia" }, {".sst" , "application/vnd.ms-pki.certstore" }, {".stl" , "application/vnd.ms-pki.stl" }, {".stm" , "text/html" }, {".sty" , "application/x-sty" }, {".svg" , "text/xml" }, {".swf" , "application/x-shockwave-flash" }, {".tdf" , "application/x-tdf" }, {".tg4" , "application/x-tg4" }, {".tga" , "application/x-tga" }, {".tif" , "image/tiff" }, {".tiff" , "image/tiff" }, {".tld" , "text/xml" }, {".top" , "drawing/x-top" }, {".torrent", "application/x-bittorrent" }, {".tsd" , "text/xml" }, {".ttf" , "application/font-woff" }, {".txt" , "text/plain" }, {".uin" , "application/x-icq" }, {".uls" , "text/iuls" }, {".vcf" , "text/x-vcard" }, {".vda" , "application/x-vda" }, {".vdx" , "application/vnd.visio" }, {".vml" , "text/xml" }, {".vpg" , "application/x-vpeg005" }, {".vsd" , "application/vnd.visio" }, {".vsd" , "application/x-vsd" }, {".vss" , "application/vnd.visio" }, {".vst" , "application/vnd.visio" }, {".vst" , "application/x-vst" }, {".vsw" , "application/vnd.visio" }, {".vsx" , "application/vnd.visio" }, {".vtx" , "application/vnd.visio" }, {".vxml" , "text/xml" }, {".wav" , "audio/wav" }, {".wax" , "audio/x-ms-wax" }, {".wb1" , "application/x-wb1" }, {".wb2" , "application/x-wb2" }, {".wb3" , "application/x-wb3" }, {".wbmp" , "image/vnd.wap.wbmp" }, {".wiz" , "application/msword" }, {".wk3" , "application/x-wk3" }, {".wk4" , "application/x-wk4" }, {".wkq" , "application/x-wkq" }, {".wks" , "application/x-wks" }, {".wm" , "video/x-ms-wm" }, {".wma" , "audio/x-ms-wma" }, {".wmd" , "application/x-ms-wmd" }, {".wmf" , "application/x-wmf" }, {".wml" , "text/vnd.wap.wml" }, {".wmv" , "video/x-ms-wmv" }, {".wmx" , "video/x-ms-wmx" }, {".wmz" , "application/x-ms-wmz" }, {".wp6" , "application/x-wp6" }, {".wpd" , "application/x-wpd" }, {".wpg" , "application/x-wpg" }, {".wpl" , "application/vnd.ms-wpl" }, {".wq1" , "application/x-wq1" }, {".wri" , "application/x-wri" }, {".wrk" , "application/x-wrk" }, {".wr1" , "application/x-wr1" }, {".ws" , "application/x-ws" }, {".ws2" , "application/x-ws" }, {".wsc" , "text/scriptlet" }, {".wsdl" , "text/xml" }, {".wvx" , "video/x-ms-wvx" }, {".xap" , "application/x-silverlight-app" }, {".xdp" , "application/vnd.adobe.xdp" }, {".xdr" , "text/xml" }, {".xfd" , "application/vnd.adobe.xfd" }, {".xfdf" , "application/vnd.adobe.xfdf" }, {".xhtml" , "text/html" }, {".xls" , "application/vnd.ms-excel" }, {".xls" , "application/x-xls" }, {".xlw" , "application/x-xlw" }, {".xml" , "text/xml" }, {".xpl" , "audio/scpls" }, {".xq" , "text/xml" }, {".xql" , "text/xml" }, {".xquery" , "text/xml" }, {".xsd" , "text/xml" }, {".xsl" , "text/xml" }, {".xslt" , "text/xml" }, {".xwd" , "application/x-xwd" }, {".x_b" , "application/x-x_b" }, {".x_t" , "application/x-x_t" } }; #endif // _MIME_H
-
resource.h
#ifndef _RESOURCE_H #define _RESOURCE_H // Search resource int searchResource(char const* path); // Identify type int identifyType(char const* path, char* type); #endif
-
resource.c
#include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/syscall.h> #include "resource.h" #include "mime.h" // Search resource int searchResource(char const* path){ return access(path, R_OK); } // Identify type int identifyType(char const* path, char* type){ char* suffix = strchr(path, '.'); if (suffix == NULL){ printf("Process:%d. Thread:%ld>>>>>> Can not get the file extension.\n", getpid(), syscall(SYS_gettid)); return -1; } for(size_t i =0; i<sizeof(s_mime)/sizeof(s_mime[0]); i++){ if (!strcasecmp(suffix, s_mime[i].suffix)){ strcpy(type, s_mime[i].type); return 0; } } printf("Process:%d. Thread:%ld>>>>>> Unable to parse resource type: %s.\n", getpid(), syscall(SYS_gettid), suffix); return -1; }
-
-
Socket Module
-
socket.h
#ifndef _SOCKET_H #define _SOCKET_H // Initialize socket int initSocket(short port); // Accept client connect int acceptClient(void); // Accept request char* recvRequest(int conn); // Send respond head int sendHead(int conn, char const* head); // Send respond body int sendBody(int conn, char const* path); // Terminate socket void terminateSocket(void); #endif
-
socket.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <pthread.h> #include <sys/resource.h> #include "server.h" #include "signals.h" #include "client.h" #include "socket.h" // Initialize the maximum number of the files static int initMaxFiles(void){ // Resource limit struct struct rlimit rl; // Get current process maximum file descriptor number if (getrlimit(RLIMIT_NOFILE, &rl) == -1){ perror("getlimit"); return -1; } // if current the maximum file descriptor number of the current process does not reach the maximum limit then if (rl.rlim_cur < rl.rlim_max){ // set the limit to max rl.rlim_cur = rl.rlim_max; if (setrlimit(RLIMIT_NOFILE, &rl) == -1){ perror("setrlimit"); return -1; } } return 0; } // Initialize server int initServer(short port){ // Initialize the maximum number of the files if (initMaxFiles() == -1){ return -1; } // Initialize signals if (initSignals() == -1){ return -1; } // Initialize socket if (initSocket(port) == -1){ return -1; } return 0; } // Run server int runServer(char const* home){ for( ; ; ){ // Accept client connection int conn = acceptClient(); if (conn == -1){ return -1; } // Client handle pthread_t tid; pthread_attr_t attr; // thread attribute pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); // set detach state CA* ca = (CA*)malloc(sizeof(CA)); ca->home = home; ca->conn = conn; int error = pthread_create(&tid, &attr, client, ca); if (error){ printf("Pthread create: %s.\n", strerror(error)); return -1; } } return 0; } // Terminate server int terminateServer(void){ // Terminate socket terminateSocket(); return 0; }
-
-
Thread Module
-
client.h
#ifndef _CLIENT_H #define _CLIENT_H // Client parameter typedef struct tag_ClientArgs{ char const* home; int conn; } CA; // Client void* client(void* arg); #endif
-
client.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/stat.h> #include <sys/syscall.h> #include "client.h" #include "socket.h" #include "http.h" #include "resource.h" // Client void* client(void* arg){ CA* ca = (CA*)arg; printf("Process:%d. Thread:%ld>>>>>> Client start.\n", getpid(), syscall(SYS_gettid)); for( ; ; ){ printf("--------------------------------------------------------------------------------------------------\n"); printf("Process:%d. Thread:%ld>>>>>> Recieve the request.\n", getpid(), syscall(SYS_gettid)); char* req = recvRequest(ca->conn); if (req == NULL) break; printf("Process:%d. Thread:%ld>>>>>> Request body\n\n%s", getpid(), syscall(SYS_gettid), req); printf("Process:%d. Thread:%ld>>>>>> Parse the request.\n", getpid(), syscall(SYS_gettid)); // Parse request HTTP_REQUEST hreq = {}; if (parseRequest(req, &hreq) == -1){ free(req); break; } free(req); // Resource request char path[PATH_MAX+1]; strcpy(path, ca->home); if (path[strlen(path)-1] == '/') path[strlen(path)-1] = '\0'; strcat(path, hreq.path); if (!strcpy(hreq.path, "/")) strcat(path, "index.html"); printf("Process:%d. Thread:%ld>>>>>> Resource path: %s.\n", getpid(), syscall(SYS_gettid), path); // Search resource HTTP_RESPOND hres = {"HTTP/1.1", 200, "OK", "text/html"}; if (searchResource(path) == -1){ hres.status = 404; strcpy(hres.description, "Not Found"); strcpy(path, "../Webserver_Home/404.html"); } else if (identifyType(path, hres.type) == -1){ hres.status = 404; strcpy(hres.description, "Not Found"); strcpy(path, "../Webserver_Home/404.html"); } struct stat st; if (stat(path, &st) == -1){ perror("stat"); break; } hres.length = st.st_size; if (strlen(hreq.connection)) strcpy(hres.connection, hreq.connection); else { if (!strcasecmp(hreq.protocol, "HTTP/1.0")) strcpy(hres.connection, "close"); else strcpy(hres.connection, "keep-alive"); } // Construct respond head char head[1024]; constructHead(&hres, head); printf("Process:%d. Thread:%ld>>>>>> Respond head.\n\n%s", getpid(), syscall(SYS_gettid), head); printf("Process:%d. Thread:%ld>>>>>> Send respond.\n", getpid(), syscall(SYS_gettid)); // Send respond head if (sendHead(ca->conn, head) == -1) break; if (sendBody(ca->conn, path) == -1) break; if (!strcasecmp(hres.connection, "close")) break; } close(ca->conn); free(ca); printf("Process:%d. Thread:%ld>>>>>> Client closed.\n", getpid(), syscall(SYS_gettid)); return NULL; }
-
-
Signal Module
-
signals.h
#ifndef _SIGNALS_H #define _SIGNALS_H // Initialize signal int initSignals(void); #endif
-
signals.c
#include <stdio.h> #include <unistd.h> #include <signal.h> #include "signals.h" // Initialize signal int initSignals(void){ printf("Process:%d>>>>>> Ignore most of the signals.\n", getpid()); for(int signum = 1; signum <= 64; ++signum){ if (signum != SIGINT && signum != SIGTERM) signal(signum, SIG_IGN); } return 0; }
-
-
Server Module
-
server.h
#ifndef _SERVER_H #define _SERVER_H // Initialize server int initServer(short port); // Run server int runServer(char const* home); // Terminate server int terminateServer(void); #endif
-
server.c
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <pthread.h> #include <sys/resource.h> #include "server.h" #include "signals.h" #include "client.h" #include "socket.h" // Initialize the maximum number of the files static int initMaxFiles(void){ // Resource limit struct struct rlimit rl; // Get current process maximum file descriptor number if (getrlimit(RLIMIT_NOFILE, &rl) == -1){ perror("getlimit"); return -1; } // if current the maximum file descriptor number of the current process does not reach the maximum limit then if (rl.rlim_cur < rl.rlim_max){ // set the limit to max rl.rlim_cur = rl.rlim_max; if (setrlimit(RLIMIT_NOFILE, &rl) == -1){ perror("setrlimit"); return -1; } } return 0; } // Initialize server int initServer(short port){ // Initialize the maximum number of the files if (initMaxFiles() == -1){ return -1; } // Initialize signals if (initSignals() == -1){ return -1; } // Initialize socket if (initSocket(port) == -1){ return -1; } return 0; } // Run server int runServer(char const* home){ for( ; ; ){ // Accept client connection int conn = acceptClient(); if (conn == -1){ return -1; } // Client handle pthread_t tid; pthread_attr_t attr; // thread attribute pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); // set detach state CA* ca = (CA*)malloc(sizeof(CA)); ca->home = home; ca->conn = conn; int error = pthread_create(&tid, &attr, client, ca); if (error){ printf("Pthread create: %s.\n", strerror(error)); return -1; } } return 0; } // Terminate server int terminateServer(void){ // Terminate socket terminateSocket(); return 0; }
-
-
Main Module
-
main.c
#include <stdio.h> #include <stdlib.h> #include "server.h" int main(int argc, char* argv[]){ // Initialize server if (initServer(argc < 2 ? 8000 : atoi(argv[1])) == -1){ return EXIT_FAILURE; } // Run server if (runServer(argc < 3 ? "./../Webserver_Home" : argv[2]) == -1){ return EXIT_FAILURE; } // Terminate server return EXIT_SUCCESS; }
-
-
Makefile
PROJ = WebServer OBJS = main.o server.o signals.o socket.o client.o http.o resource.o CC = gcc LINK = gcc RM = rm -rf CFLAGS = -c -Wall -I. LIBS = -lpthread $(PROJ): $(OBJS) $(LINK) $^ $(LIBS) -o $@ .c.o: $(CC) $(CFLAGS) $^ -o $@ clean: $(RM) $(PROJ) $(OBJS)
GitHub: https://github.com/2790004z/Linux_WebServer_C_Language.git