📜 ⬆️ ⬇️

C / C ++ web application with FastCGI - it’s just

Good day.
In this article I would like to tell you about the FastCGI protocol and how to work with it. Despite the fact that the protocol itself and its implementation appeared as early as 1996, there are simply no detailed manuals for this protocol - the developers did not write a certificate for their own library. But about two years ago, when I first started using this protocol, I often met phrases like “I don’t quite understand how to use this library.” It is this flaw that I want to correct - to write a detailed guide to using this protocol in a multi-threaded program and recommendations on the choice of various parameters that could be used by everyone.

The good news is that the method of encoding data in FastCGI and in CGI is the same; only the way they are transferred changes: if the CGI program uses the standard I / O interface, then the FastCGI program is sockets. In other words, you just need to deal with several functions of the library to work with FastCGI, and then just use the experience of writing CGI programs, examples of which, fortunately, are many.

So, in this article we will look at:
- What is FastCGI and how is it different from the CGI protocol
- Why do I need FastCGI, when there are already many languages ​​for web development
- What implementations of the FastCGI protocol exist
- What are sockets
- Description of the functions of the library FastCGI
- A simple example of a multithreaded FastCGI program
- A simple example of Nginx configuration
Unfortunately, it is very difficult to write an article that is equally understandable for beginners and interesting for experienced old-timers, so I will try to cover all the moments in as much detail as possible, and you can just skip sections that are of no interest to you.

What is FastCGI?


About FastCGI can be read on Wikipedia . In a nutshell, this is a CGI program running in a loop. If a regular CGI program is re-launched for each new request, then the FastCGI program uses a queue of requests that are processed sequentially. Now imagine: your 4-8-core server received 300-500 simultaneous requests. A regular CGI program will be launched for execution these same 300-500 times. Obviously, there are too many such number of processes - your server will not physically be able to work them all at once. This means that you will have a queue of processes waiting for their own CPU time quantum. Usually the scheduler will distribute the processor evenly (as in this case, the priorities of all processes are the same), which means you will have 300-500 "almost ready" responses to requests. It sounds somehow not very optimistic, is not it? In the FastCGI program, all these problems are solved by a simple request queue (that is, query multiplexing is used).
')

Why do I need FastCGI when I already have PHP, Ruby, Python, Perl, etc.?


Perhaps the main reason is that the compiled program will run faster than the interpreted one. For PHP, for example, there is a whole line of accelerators, among which are APC, eAccelerator, XCache, which reduce the time to interpret the code. But for C / C ++, all this is simply not necessary.
The second thing you need to remember is that dynamic typing and garbage collection take up a lot of resources. Sometimes a lot. For example, arrays of integers in PHP occupy approximately 18 times more memory (up to 35 times depending on various PHP compilation parameters) than in C / C ++ for the same amount of data, so think about overhead for relatively large data structures.
Third, the FastCGI program can store data common to different requests. For example, if PHP starts a request from scratch every time, the FastCGI program can do a number of preparatory actions even before the first request is received, for example, allocate memory, load frequently used data, etc. - obviously, all this can improve the overall performance of the system.
Fourth - scalability. If mod_php assumes that the Apache web server and PHP are on the same machine, then the FastCGI application can use TCP sockets. In other words, you can have a whole cluster of several machines that are connected through the network. At the same time, FastCGI also supports Unix domain sockets, which allows, if necessary, to run the FastCGI application and the web server effectively on the same machine.
The fifth is security. You will not believe it, but with the default settings, Apache allows you to do everything. For example, if an attacker uploads a malicious exploit.php.jpg script under the guise of an “innocent picture” and then opens it in a browser, Apache “honestly” executes the malicious php code. Perhaps the only sufficiently reliable solution is to remove or change all potentially dangerous extensions from the names of the downloaded files, in this case php, php4, php5, phtml, etc. This technique is used, for example, in Drupal - an underscore is added to all the “additional” extensions and exploit.php_.jpg is obtained. However, it should be noted that the system administrator can add any additional file extension as a php processor, so that any .html can suddenly turn into a terrible security hole only because .php looked ugly, was bad for SEO or did not like the customer. So what does FastCGI give us in terms of security? First, if you use the Nginx web server instead of Apache, it will simply give out static files. Point. In other words, the exploit.php.jpg file will be given “as is”, without any processing on the server side, so it is impossible to launch a malicious script. Secondly, the FastCGI program and the web server can work from under different users, and therefore they will have different rights to files and folders. For example, a web server can only read downloaded files — this is enough to return static data, and a FastCGI program can only read and change the contents of the folder with downloaded files — this is enough to load new files and delete old files, but directly access the downloaded files themselves. will not have, and therefore will not be able to execute malicious code. Third, the FastCGI program can work in a chroot different from the chroot of the web server. By itself, chroot (changing the root directory) allows you to severely restrict the rights of the program, that is, increase the overall security of the system, because the program simply cannot access files outside the specified directory.

Which web server with FastCGI support should I choose?


In short - I use Nginx . In general, there are quite a few servers with FastCGI support, including commercial ones, so let me consider a few alternatives.
Apache is perhaps the first thing that comes to mind, although it consumes much more resources than Nginx. For example, for 10,000 inactive HTTP keep-alive connections, Nginx consumes about 2.5M of memory, which is quite realistic even for a relatively weak machine, and Apache is forced to create a new stream for each new connection, so 10,000 streams are fantastic.
Lighttpd - the main disadvantage of this web server is that it processes all requests in one stream. This means that there may be problems with scalability - you simply can not use all 4-8 cores of modern processors. And second, if for some reason the web server's thread hangs (for example, due to the long wait for a response from the hard disk), the whole server will hang. In other words, all other clients will stop receiving responses due to one slow request.
Another candidate is Cherokee . According to the developers, in some cases Nginx and Lighttpd are faster.

What is the implementation of the FastCGI protocol?


At the moment there are two implementations of the FastCGI protocol — the libfcgi.lib library from the creators of the FastCGI protocol, and Fastcgi ++ — the class library in C ++. Libfcgi has been developed since 1996 and, according to Open Market, is very stable, moreover it is more common, so we’ll use it in this article. It should be noted that the library is written in C, the built-in “wrapper” of C ++ cannot be called high-level, so we will use the C-interface.
I think it makes no sense to stop the installation of the library itself - there is a makefile in it, so there should be no problems. In addition, in popular distributions this library is available from packages.

What are sockets?


The general concept of sockets can be obtained in Wikipedia . In a nutshell, sockets are interprocess communication.
As we remember, in all modern operating systems, each process uses its own address space. The core of the operating system is responsible for direct access to the RAM, and if the program is contacted by a non-existent (in the context of this program) memory address, the kernel will return a segmentation fault and close the program. This is great - now errors in one program simply cannot hurt another - they are, as it were, in other dimensions. But since programs have a different address space, there can be no other way from shared data or data exchange. And if you really need to transfer data from one program to another, how then? Actually, to solve this problem, sockets were developed - two or more processes (read: programs) connect to the same socket and begin data exchange. It turns out a sort of “window” to another world - through it you can receive and send data to other streams.
Depending on the type of connection used, the sockets are different. For example, there are TCP sockets - they use a normal network for data exchange, that is, programs can work on different computers. The second most common option - Unix domain sockets (Unix domain socket) - is suitable for data exchange only within one machine and looks like a normal path in the file system, but the actual hard disk is not used - all data exchange takes place in RAM. Due to the fact that you do not need to use a network stack, they work somewhat faster (by about 10%) than TCP sockets. For Windows, this socket option is called a named pipe (named pipe).
Examples of using sockets for the GNU / Linux OS can be found in this article . If you have not worked with sockets yet, I would recommend to familiarize yourself with it - this is not mandatory, but it will improve the understanding of the things outlined here.

How to use libfcgi library?


So, we want to create a multithreaded FastCGI application, so let me describe a number of the most important functions.
First of all, the library needs to be initialized:
int FCGX_Init(void); 

Attention! This function must be called before any other functions of this library and only once (only once, for any number of threads).

Next we need to open the listening socket:
 int FCGX_OpenSocket(const char *path, int backlog); 

The path variable contains the socket connection string. Both Unix domain sockets and TCP sockets are supported, the library itself will do all the necessary work on preparing parameters and calling a function.
Examples of connection strings for Unix domain sockets:
 "/tmp/fastcgi/mysocket" "/tmp/fcgi_example.bare.sock" 

I think everything is clear: you just need to pass a unique path as a string, while all processes interacting with the socket should have access to it. Once again: this method works only within one computer, but somewhat faster than TCP sockets.
Examples of connection strings for TCP sockets:
 ":5000" ":9000" 

In this case, a TCP socket is opened on the specified port (in this case, 5000 or 9000, respectively), and requests will be received from any IP address. Attention! This method is potentially unsafe - if your server is connected to the Internet, your FastCGI program will receive requests from any other computer. This means that any attacker can send a “death package” to your FastCGI program. Of course, there is nothing good about it - at best, your program can simply “fall” and you will get a denial of service (DoS attack, if you want), at worst, remote code execution (this is if you’re not lucky at all), so always limit access to such ports using a firewall (firewall), while access should be provided only to those IP addresses that are actually used during normal operation of the FastCGI program (the principle “everything is forbidden is not explicitly allowed”).
The following is an example of connection strings:
 "*:5000" "*:9000" 

The method is completely similar to the previous one: a TCP socket is opened with receiving connections from any IP address, so in this case it is also necessary to carefully configure the firewall. The only plus from such a connection string is administratively pure - any programmer or system administrator reading the configuration files will understand that your program accepts connections from any IP address, therefore, all other things being equal, it is better to prefer this to the previous one.
A safer option is to explicitly specify the IP address in the connection string:
 "5.5.5.5:5000" "127.0.0.1:9000" 

In this case, requests will be accepted only from the specified IP address (in this case, 5.5.5.5 or 127.0.0.1, respectively), for all other IP addresses this port (in this case, 5000 or 9000, respectively) will be closed. This increases the overall security of the system, so whenever possible, always use this format for connecting to TCP sockets - what if the system administrator “just forgets” to configure the firewall? Please pay attention to the second example - the address of the same machine (localhost) is indicated there. This allows you to create a TCP socket on the same machine, if for some reason you cannot use Unix domain sockets (for example, because the chroot of the web server and the chroot FastCGI programs are in different folders and do not have shared file paths ). Unfortunately, you cannot specify two or more different IP addresses, so if you really need to accept requests from several web servers located on different computers, you will have to either open the port completely (see the previous method) and rely on your firewall, or use multiple sockets on different ports. Also, the libfcgi library does not support IPv6 addresses — back in 1996, this standard was just born, so you have to limit your appetites to regular IPv4 addresses. However, if you really need IPv6 support, it is relatively easy to add it by patching the FCGX_OpenSocket function — the library license allows it.
Attention! Using the function of specifying an IP address when creating a socket is not sufficient protection - IP spoofing attacks are possible (spoofing the IP address of the sender of the packet), so setting up the firewall is still required. Usually, as a protection against IP spoofing, the firewall checks the correspondence between the IP address of the packet and the MAC address of the network card for all hosts on our local network (more precisely, for the broadcast domain with our host), and discards all packets coming from the Internet whose return address located in the zone of private IP addresses or local host (masks 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, fc00 :: / 7, 127.0.0.0/8 and :: 1/128). However, it is still better to use this library feature - in case of an incorrectly configured firewall, it is much more difficult to send a “death packet” from a forged IP address than from any, because the TCP protocol has built-in protection against IP spoofing.
The last type of connection string is to use the domain name of the host:
 "example.com:5000" "localhost:9000" 

In this case, the IP address will be automatically obtained based on the domain name of the specified host. Restrictions are the same - a single IPv4 address must match the host, otherwise an error will occur. However, given that the socket is created once at the very beginning of working with FastCGI, this method is unlikely to be very useful - it will still not be possible to dynamically change the IP address (more precisely, after each change of IP address you will have to restart your FastCGI program). On the other hand, it may be useful for a relatively large network - remembering a domain name is still easier than an IP address.

The second parameter of the backlog function determines the length of the socket request queue. The special value 0 (zero) means the default queue length for this operating system.
Every time a request comes from a web server, a new connection is put in this queue, awaiting processing by our FastCGI program. If the queue is full, all subsequent connection requests will fail — the web server will receive a Connection refused response. In principle, there is nothing wrong with that - the Nginx web server has its own request queue, and if there are no free resources, new requests will wait for their turn to be processed already in the web server queue (at least until timeout expires). In addition, if you have several servers running a FastCGI program, Nginx can transfer such a request to a less loaded server.
So let's try to figure out how long the queue will be optimal. In general, it is better to configure this parameter individually based on load testing data, but we will try to estimate the most appropriate range for this value. The first thing you need to know is that the maximum queue length is limited (determined by the settings of the operating system kernel, usually no more than 1024 connections). Secondly, the queue consumes resources, cheap, but still resources, so you should not make it unreasonably long. Further, let's say our FastCGI program has 8 worker threads (quite realistic for modern 4-8 core processors), and each thread needs its own connection - tasks are processed in parallel. So, ideally, we should already have 8 requests from the web server, so that immediately, without unnecessary delays, to ensure that all threads work. In other words, the minimum size of the request queue is the number of worker threads of the FastCGI program. You can try to increase this value by 50% -100% to provide some margin on loading, since the time of data transmission over the network is of course.
Now let's define the upper limit of this value. Here you need to know how many requests we can actually process and limit the queue of requests to this value. Imagine that you made this queue too long - so much so that your customers just get tired of waiting for their turn and they just leave your site without waiting for an answer. Obviously, there is nothing good about this - the web server had to send a request to open a connection, which in itself is expensive, and then also close this connection only because the FastCGI program did not have enough time to process this request. In a word, we just waste processor time, and yet we just don’t have enough of it! But this is not the worst thing - worse, when the client refused to receive information from your site, the field already started processing the request. It turns out that we will have to fully process in essence the already unnecessary request, which, you see, will only worsen the situation. Theoretically, a situation may arise where most of the clients will not wait for an answer when your processor is 100% loaded. Not good.
So, we admit one request we can process in 300 milliseconds (that is, 0.3 seconds). Further, we know that on average 50% of visitors leave a resource if a web page loads more than 30 seconds. Obviously, 50% of those who are dissatisfied are too much, so we limit the maximum page load time to 5 seconds. In this case, a fully finished web page is meant - after applying cascading style sheets and executing JavaScript - this stage on an average website can occupy 70% of the total load time of a web page. So, no more than 5 * 0.3 = 1.5 seconds remained for downloading data over the network. Further it should be remembered that the html-code, style sheets, scripts and graphics are transmitted in different files, and first - the html-code, and then everything else. However, after receiving the html-code, the browser begins to request the remaining resources in parallel, so that you can estimate the loading time of the html-code as 50% of the total time for receiving data. So, we have no more than 1.5 * 0.5 = 0.75 seconds left to process a single request. If, on average, one thread processes a request in 0.3 seconds, then the queue should have 0.75 / 0.3 = 2.5 requests for the stream. Since we have 8 worker threads, the resulting queue size should be 2.5 * 8 = 20 requests. I would like to note the conventionality of the above calculations - if there is a specific site, the values ​​used in the calculation can be determined much more accurately, but it still gives a starting point for a more optimal performance tuning.

So, we received a socket descriptor, after that it is necessary to allocate memory for the request structure. The description of this structure is as follows:
 typedef struct FCGX_Request { int requestId; int role; FCGX_Stream *in; FCGX_Stream *out; FCGX_Stream *err; char **envp; struct Params *paramsPtr; int ipcFd; int isBeginProcessed; int keepConnection; int appStatus; int nWriters; int flags; int listen_sock; int detached; } FCGX_Request; 

Attention! After receiving a new request, all previous data will be lost, so if you need long-term storage of data, use deep copying (copy the data itself, not the data pointers).
You should know the following about this structure:
- the variables in, out, and err play the role of input, output, and error streams, respectively. The input stream contains data from the POST request, the FastCGI program needs to be sent to the output stream (for example, http-headers and html-code of the web page), and the error stream simply adds to the error log of the web server. In this case, the flow of errors can not be used at all - if you really need to log errors, then perhaps it is better to use a separate file for this - data transmission over the network and their subsequent processing by the web server consumes additional resources.
- the envp variable contains the values ​​of the environment variables set by the web server and http-headers, for example: SERVER_PROTOCOL, REQUEST_METHOD, REQUEST_URI, QUERY_STRING, CONTENT_LENGTH, HTTP_USER_AGENT, HTTP_COOKIE, HTTP_REFERER, and so on. These headers are defined respectively by the CGI and HTTP protocols standards, examples of their use can be found in any CGI program. The data itself is stored in an array of strings, with the last element of the array containing a null pointer (NULL) as an indication of the end of the array. Each line (each element of the array of strings) contains one variable value in the format NAME_VARIABLE = VALUE, for example: CONTENT_LENGTH = 0 (in this case means that this request does not have POST data, since their length is zero). If in the array of strings envp there is no header you need, then it was not transferred. If you want to get all the variable values ​​passed to the FastCGI program, just loop through all the lines in the envp array until you see a pointer to NULL.
Actually, we finished this with the description of this structure - you will not need all the other variables.

Memory allocated, now you need to initialize the query structure:
 int FCGX_InitRequest(FCGX_Request *request, int sock, int flags); 

The function parameters are as follows:
request - a pointer to the data structure to be initialized
sock is the socket descriptor that we received after calling the FCGX_OpenSocket function. It should be noted that instead of the ready-made descriptor, you can pass 0 (zero) and get a socket with the default settings, but for us this method is not at all interesting - the socket will be open on a random free port, which means we can’t properly configure our web server - we do not know in advance exactly where to send the data.
flags - flags. Actually, only one flag can be passed to this function - FCGI_FAIL_ACCEPT_ON_INTR - do not call FCGX_Accept_r when breaking.

After that you need to get a new request:
 int FCGX_Accept_r(FCGX_Request *request); 

It is necessary to pass to it the request structure already initialized at the previous stage. Attention! In a multi-threaded program, you must use synchronization when calling this function.
Actually, this function does all the work on working with sockets: first, it sends a response to the web server to the previous request (if there was one), closes the previous data channel and frees all the resources associated with it (including request structure variables) then it receives a new request, opens a new data transmission channel and prepares new data in the request structure for subsequent processing. In case of an error in receiving a new request, the function returns an error code less than zero.

Next, you will probably need to get the environment variables, for this you can either independently process the request-> envp array, or use the function
 char *FCGX_GetParam(const char *name, FCGX_ParamArray envp); 

where name is a string containing the name of the environment variable or http header whose value you want to receive,
envp is an array of environment variables contained in the variable request-> envp
The function returns the value of the environment variable we need as a string. Let the attentive reader not be afraid of the type mismatch between char ** and FCGX_ParamArray - these types are declared synonymous (typedef char ** FCGX_ParamArray).
In addition, you will probably need to send a response to the web server. To do this, use the request-> out output stream and the function
 int FCGX_PutStr(const char *str, int n, FCGX_Stream *stream); 

where str is the buffer containing the data to be output, without a terminating zero (that is, the buffer may contain binary data),
n is the buffer length in bytes
stream is the stream to which we want to output data (request-> out or request-> err).

If you use standards C-lines with a terminating zero, it will be more convenient to use the function
 int FCGX_PutS(const char *str, FCGX_Stream *stream); 

which simply determines the length of the string by the function strlen (str) and calls the previous function. Therefore, if you know the length of a string beforehand (for example, you use C ++ std :: string strings), it is better to use the previous function for efficiency reasons.
It should be noted that these functions work fine with UTF-8 strings, so there should be no problems with multilingual web applications.
You can also call these functions several times while processing the same request, in some cases it can improve performance. For example, you need to send some big file. Instead of downloading the entire file from the hard disk, and then sending it in one piece, you can immediately start sending data. As a result, the client, instead of the white screen of the browser, will begin to receive the data of interest to him, which psychologically makes him wait a little longer. In other words, you kind of win some time to load the page. I would also like to note that most of the resources (cascading style sheets, JavaScript, etc.) are indicated at the beginning of the web page, that is, the browser will be able to analyze part of the html-code and start downloading these resources earlier - another reason to display data piecemeal.

The next thing you need is to process the POST request. In order to get its value, you need to read the data from the request-> in stream using the function
 int FCGX_GetStr(char * str, int n, FCGX_Stream *stream); 

where str is a pointer to the buffer,
n is the size of the buffer in bytes
stream is the stream from which we read the data.
The size of the transmitted data in a POST request (in bytes) can be determined using the environment variable CONTENT_LENGTH, the value of which, as we remember, can be obtained using the FCGX_GetParam function. Attention! Creating a str buffer based on the value of the CONTENT_LENGTH variable without any restrictions is a very bad idea: any attacker can send any, arbitrarily large POST request, and your server can simply run out of free RAM (a DoS attack will turn out, if you wish). Instead, it is better to limit the size of the buffer to some reasonable value (from several kilobytes to several megabytes) and call the function FCGX_GetStr several times.

The last important function flashes output and error streams (the data that we still have not yet sent to the output and error streams are sent to the client) and closes the connection:
 void FCGX_Finish_r(FCGX_Request *request); 

I would like to especially note that this function is not mandatory: the FCGX_Accept_r function also sends data to the client and closes the current connection before receiving a new request. The question is: then why is it needed?Imagine that you have already sent the client all the necessary data, and now you need to perform some final operations: write statistics to the database, errors to the log file, etc. Obviously, the connection with the client is no longer needed, but the client (in the sense of the browser) is still waiting for information from us: what if we send something else? It is obvious that we cannot call FCGX_Accept_r ahead of time - after that, we will need to start processing the next request. Just in this case, you will need the function FCGX_Finish_r - it will allow you to close the current connection before receiving a new request. Yes, we will be able to process the same number of requests per unit of time as without using this function, but the client will receive an answer earlier - he will not have to wait until the end of the execution of our final operations,But it is precisely because of the greater speed of processing requests that we use FastCGI.
This, in fact, ends the description of the library functions and begins processing the data received.

A simple example of a multithreaded FastCGI program


I think in the example everything will be clear. The only printing of debugging messages and “falling asleep” of the workflow are made solely for demonstration purposes. When compiling a program, do not forget to include the libfcgi and libpthread libraries (the gcc compiler options: -lfcgi and -lpthread).

 #include <pthread.h> #include <sys/types.h> #include <stdio.h> #include "fcgi_config.h" #include "fcgiapp.h" #define THREAD_COUNT 8 #define SOCKET_PATH "127.0.0.1:9000" //    static int socketId; static void *doit(void *a) { int rc, i; FCGX_Request request; char *server_name; if(FCGX_InitRequest(&request, socketId, 0) != 0) { //     printf("Can not init request\n"); return NULL; } printf("Request is inited\n"); for(;;) { static pthread_mutex_t accept_mutex = PTHREAD_MUTEX_INITIALIZER; //    printf("Try to accept new request\n"); pthread_mutex_lock(&accept_mutex); rc = FCGX_Accept_r(&request); pthread_mutex_unlock(&accept_mutex); if(rc < 0) { //    printf("Can not accept new request\n"); break; } printf("request is accepted\n"); //   server_name = FCGX_GetParam("SERVER_NAME", request.envp); //  HTTP- (    ) FCGX_PutS("Content-type: text/html\r\n", request.out); //         FCGX_PutS("\r\n", request.out); //   ( - html- -) FCGX_PutS("<html>\r\n", request.out); FCGX_PutS("<head>\r\n", request.out); FCGX_PutS("<title>FastCGI Hello! (multi-threaded C, fcgiapp library)</title>\r\n", request.out); FCGX_PutS("</head>\r\n", request.out); FCGX_PutS("<body>\r\n", request.out); FCGX_PutS("<h1>FastCGI Hello! (multi-threaded C, fcgiapp library)</h1>\r\n", request.out); FCGX_PutS("<p>Request accepted from host <i>", request.out); FCGX_PutS(server_name ? server_name : "?", request.out); FCGX_PutS("</i></p>\r\n", request.out); FCGX_PutS("</body>\r\n", request.out); FCGX_PutS("</html>\r\n", request.out); //"" -    sleep(2); //   FCGX_Finish_r(&request); //  -  ,    .. } return NULL; } int main(void) { int i; pthread_t id[THREAD_COUNT]; //  FCGX_Init(); printf("Lib is inited\n"); //   socketId = FCGX_OpenSocket(SOCKET_PATH, 20); if(socketId < 0) { //    return 1; } printf("Socket is opened\n"); //   for(i = 0; i < THREAD_COUNT; i++) { pthread_create(&id[i], NULL, doit, NULL); } //    for(i = 0; i < THREAD_COUNT; i++) { pthread_join(id[i], NULL); } return 0; } 


Simple example of Nginx configuration


Actually, the simplest example of a config looks like this:

 server { 
	server_name localhost; 

	location / { 
		fastcgi_pass 127.0.0.1:9000; 
		#fastcgi_pass unix: / tmp / fastcgi / mysocket; 
		#fastcgi_pass localhost: 9000; 
		 
		include fastcgi_params; 
	 } 
 } 


In this case, this config is enough for the correct operation of our FastCGI program. Commented lines are an example of working with respectively Unix domain sockets and specifying the domain name of the host instead of the IP address.
After compiling and running the program, and setting up Nginx, I got a proud inscription at localhost:
FastCGI Hello! (multi-threaded C, fcgiapp library)

Thanks to everyone who read to the end.

Source: https://habr.com/ru/post/154187/


All Articles