📜 ⬆️ ⬇️

libscgi is an effective solution for simple and fast scripts

It is often necessary to implement a simple easy solution that should work out pretty quickly. And with the use of technology AJAX it has become even more relevant. It can be an autocompet script, a specific search script, information output from the directory. Previously used cgi scripts. Under heavy loads, they were not very effective and the fcgi and scgi protocols were developed. It should be noted that the performance of the scgi server is quite high (more than 1500 loops / sec) and memory takes only 600K.

The Simple Common Gateway Interface (SCGI) protocol is a protocol for application interaction with web (http) servers. Most modern WEB servers (Apache / nginx / lighttpd) have built-in support for scgi. Below is a brief description of the use of a simple library, which is a scgi server.

Sources here .

Protocol Description.

The client connects to the SCGI server via a streaming protocol that allows the transmission of 8-bit bytes. A request consists of several headers and a request body (POST DATA). The request data transfer format has the following pattern: [len header]: \r\n
[header parm name] \0 [header parm value] \0 \r\n
[header parm name] \0 [header parm value] \0 \r\n
...
,\r\n
[POST DATA]
[len header]: \r\n
[header parm name] \0 [header parm value] \0 \r\n
[header parm name] \0 [header parm value] \0 \r\n
...
,\r\n
[POST DATA]


The first header must have the name "CONTENT_LENGTH", and the length of the message body (in decimal representation) must be indicated in its value. The "CONTENT_LENGTH" header should always be transmitted, even if its value is 0 (all GET requests). Between the headers and the body of the request should be a comma and a newline. The body (POST / PUT data) is sent for the headers and its length should be determined by the "CONTENT_LENGTH" header.
')
Nginx configuration
In the manual, an instruction should be attached to each WEB server on how to connect this or that module. I set up my scgi server under nginx.
Configuration example for a specific location, port 8080.
location /dictionary {
scgi_pass localhost:8080;
include scgi_params;
}

Internal device scgi server and its use

The server is implemented on the basis of libevent. Each used url (DOCUMENT_URI header) has its own handler.
scgi.addHandler("/post", reinterpret_cast<IScgiHandler *>(new Handler1()));

Each custom handler must be defined in the run () method of the inheritor of the IScgiHandler class.
In this method, the parameters are passed:
map< string,string > * parms - ,
char * buffUot - .

All received POST data is stored in parms with the key "POST_DATA".
Access to input parameters is simplified using the protected method: getParam (string parnName, map <string, string> * parms);

Consider a sample handler code:

  1. class Handler1 : public IScgiHandler { void run ( map < string,string > * parms, char * buffUot ) { string parm = getParam ( "REQUEST_METHOD" ,parms ) ; // POST if ( parm == "POST" ) { // WEB-client POST strcpy ( buffUot, getParam ( "POST_DATA" ,parms ) . c_str ( ) ) ; return ; } // WEB-client QUERY_STRING strcpy ( buffUot, getParam ( "QUERY_STRING" ,parms ) . c_str ( ) ) ; } } ;
  2. class Handler1 : public IScgiHandler { void run ( map < string,string > * parms, char * buffUot ) { string parm = getParam ( "REQUEST_METHOD" ,parms ) ; // POST if ( parm == "POST" ) { // WEB-client POST strcpy ( buffUot, getParam ( "POST_DATA" ,parms ) . c_str ( ) ) ; return ; } // WEB-client QUERY_STRING strcpy ( buffUot, getParam ( "QUERY_STRING" ,parms ) . c_str ( ) ) ; } } ;
  3. class Handler1 : public IScgiHandler { void run ( map < string,string > * parms, char * buffUot ) { string parm = getParam ( "REQUEST_METHOD" ,parms ) ; // POST if ( parm == "POST" ) { // WEB-client POST strcpy ( buffUot, getParam ( "POST_DATA" ,parms ) . c_str ( ) ) ; return ; } // WEB-client QUERY_STRING strcpy ( buffUot, getParam ( "QUERY_STRING" ,parms ) . c_str ( ) ) ; } } ;
  4. class Handler1 : public IScgiHandler { void run ( map < string,string > * parms, char * buffUot ) { string parm = getParam ( "REQUEST_METHOD" ,parms ) ; // POST if ( parm == "POST" ) { // WEB-client POST strcpy ( buffUot, getParam ( "POST_DATA" ,parms ) . c_str ( ) ) ; return ; } // WEB-client QUERY_STRING strcpy ( buffUot, getParam ( "QUERY_STRING" ,parms ) . c_str ( ) ) ; } } ;
  5. class Handler1 : public IScgiHandler { void run ( map < string,string > * parms, char * buffUot ) { string parm = getParam ( "REQUEST_METHOD" ,parms ) ; // POST if ( parm == "POST" ) { // WEB-client POST strcpy ( buffUot, getParam ( "POST_DATA" ,parms ) . c_str ( ) ) ; return ; } // WEB-client QUERY_STRING strcpy ( buffUot, getParam ( "QUERY_STRING" ,parms ) . c_str ( ) ) ; } } ;
  6. class Handler1 : public IScgiHandler { void run ( map < string,string > * parms, char * buffUot ) { string parm = getParam ( "REQUEST_METHOD" ,parms ) ; // POST if ( parm == "POST" ) { // WEB-client POST strcpy ( buffUot, getParam ( "POST_DATA" ,parms ) . c_str ( ) ) ; return ; } // WEB-client QUERY_STRING strcpy ( buffUot, getParam ( "QUERY_STRING" ,parms ) . c_str ( ) ) ; } } ;
  7. class Handler1 : public IScgiHandler { void run ( map < string,string > * parms, char * buffUot ) { string parm = getParam ( "REQUEST_METHOD" ,parms ) ; // POST if ( parm == "POST" ) { // WEB-client POST strcpy ( buffUot, getParam ( "POST_DATA" ,parms ) . c_str ( ) ) ; return ; } // WEB-client QUERY_STRING strcpy ( buffUot, getParam ( "QUERY_STRING" ,parms ) . c_str ( ) ) ; } } ;
  8. class Handler1 : public IScgiHandler { void run ( map < string,string > * parms, char * buffUot ) { string parm = getParam ( "REQUEST_METHOD" ,parms ) ; // POST if ( parm == "POST" ) { // WEB-client POST strcpy ( buffUot, getParam ( "POST_DATA" ,parms ) . c_str ( ) ) ; return ; } // WEB-client QUERY_STRING strcpy ( buffUot, getParam ( "QUERY_STRING" ,parms ) . c_str ( ) ) ; } } ;
  9. class Handler1 : public IScgiHandler { void run ( map < string,string > * parms, char * buffUot ) { string parm = getParam ( "REQUEST_METHOD" ,parms ) ; // POST if ( parm == "POST" ) { // WEB-client POST strcpy ( buffUot, getParam ( "POST_DATA" ,parms ) . c_str ( ) ) ; return ; } // WEB-client QUERY_STRING strcpy ( buffUot, getParam ( "QUERY_STRING" ,parms ) . c_str ( ) ) ; } } ;
  10. class Handler1 : public IScgiHandler { void run ( map < string,string > * parms, char * buffUot ) { string parm = getParam ( "REQUEST_METHOD" ,parms ) ; // POST if ( parm == "POST" ) { // WEB-client POST strcpy ( buffUot, getParam ( "POST_DATA" ,parms ) . c_str ( ) ) ; return ; } // WEB-client QUERY_STRING strcpy ( buffUot, getParam ( "QUERY_STRING" ,parms ) . c_str ( ) ) ; } } ;
  11. class Handler1 : public IScgiHandler { void run ( map < string,string > * parms, char * buffUot ) { string parm = getParam ( "REQUEST_METHOD" ,parms ) ; // POST if ( parm == "POST" ) { // WEB-client POST strcpy ( buffUot, getParam ( "POST_DATA" ,parms ) . c_str ( ) ) ; return ; } // WEB-client QUERY_STRING strcpy ( buffUot, getParam ( "QUERY_STRING" ,parms ) . c_str ( ) ) ; } } ;
  12. class Handler1 : public IScgiHandler { void run ( map < string,string > * parms, char * buffUot ) { string parm = getParam ( "REQUEST_METHOD" ,parms ) ; // POST if ( parm == "POST" ) { // WEB-client POST strcpy ( buffUot, getParam ( "POST_DATA" ,parms ) . c_str ( ) ) ; return ; } // WEB-client QUERY_STRING strcpy ( buffUot, getParam ( "QUERY_STRING" ,parms ) . c_str ( ) ) ; } } ;
  13. class Handler1 : public IScgiHandler { void run ( map < string,string > * parms, char * buffUot ) { string parm = getParam ( "REQUEST_METHOD" ,parms ) ; // POST if ( parm == "POST" ) { // WEB-client POST strcpy ( buffUot, getParam ( "POST_DATA" ,parms ) . c_str ( ) ) ; return ; } // WEB-client QUERY_STRING strcpy ( buffUot, getParam ( "QUERY_STRING" ,parms ) . c_str ( ) ) ; } } ;


Starting the server is pretty simple:
  1. // scgiServer scgi ; // pid_t pid ; if ( ( pid = scgi. demonize ( ) ) < 1 ) { if ( pid == - 1 ) { cerr << "demonize error \n " ; return 1 ; } return 0 ; } // if ( scgi. init ( "127.0.0.1" , 8080 ) ) { cerr << "server stopped \n " ; return 1 ; } // scgi. addHandler ( "/post" , reinterpret_cast < IScgiHandler * > ( new Handler1 ( ) ) ) ; scgi. addHandler ( "/xxx" , reinterpret_cast < IScgiHandler * > ( new Handler2 ( ) ) ) ; scgi. run ( ) ;
  2. // scgiServer scgi ; // pid_t pid ; if ( ( pid = scgi. demonize ( ) ) < 1 ) { if ( pid == - 1 ) { cerr << "demonize error \n " ; return 1 ; } return 0 ; } // if ( scgi. init ( "127.0.0.1" , 8080 ) ) { cerr << "server stopped \n " ; return 1 ; } // scgi. addHandler ( "/post" , reinterpret_cast < IScgiHandler * > ( new Handler1 ( ) ) ) ; scgi. addHandler ( "/xxx" , reinterpret_cast < IScgiHandler * > ( new Handler2 ( ) ) ) ; scgi. run ( ) ;
  3. // scgiServer scgi ; // pid_t pid ; if ( ( pid = scgi. demonize ( ) ) < 1 ) { if ( pid == - 1 ) { cerr << "demonize error \n " ; return 1 ; } return 0 ; } // if ( scgi. init ( "127.0.0.1" , 8080 ) ) { cerr << "server stopped \n " ; return 1 ; } // scgi. addHandler ( "/post" , reinterpret_cast < IScgiHandler * > ( new Handler1 ( ) ) ) ; scgi. addHandler ( "/xxx" , reinterpret_cast < IScgiHandler * > ( new Handler2 ( ) ) ) ; scgi. run ( ) ;
  4. // scgiServer scgi ; // pid_t pid ; if ( ( pid = scgi. demonize ( ) ) < 1 ) { if ( pid == - 1 ) { cerr << "demonize error \n " ; return 1 ; } return 0 ; } // if ( scgi. init ( "127.0.0.1" , 8080 ) ) { cerr << "server stopped \n " ; return 1 ; } // scgi. addHandler ( "/post" , reinterpret_cast < IScgiHandler * > ( new Handler1 ( ) ) ) ; scgi. addHandler ( "/xxx" , reinterpret_cast < IScgiHandler * > ( new Handler2 ( ) ) ) ; scgi. run ( ) ;
  5. // scgiServer scgi ; // pid_t pid ; if ( ( pid = scgi. demonize ( ) ) < 1 ) { if ( pid == - 1 ) { cerr << "demonize error \n " ; return 1 ; } return 0 ; } // if ( scgi. init ( "127.0.0.1" , 8080 ) ) { cerr << "server stopped \n " ; return 1 ; } // scgi. addHandler ( "/post" , reinterpret_cast < IScgiHandler * > ( new Handler1 ( ) ) ) ; scgi. addHandler ( "/xxx" , reinterpret_cast < IScgiHandler * > ( new Handler2 ( ) ) ) ; scgi. run ( ) ;
  6. // scgiServer scgi ; // pid_t pid ; if ( ( pid = scgi. demonize ( ) ) < 1 ) { if ( pid == - 1 ) { cerr << "demonize error \n " ; return 1 ; } return 0 ; } // if ( scgi. init ( "127.0.0.1" , 8080 ) ) { cerr << "server stopped \n " ; return 1 ; } // scgi. addHandler ( "/post" , reinterpret_cast < IScgiHandler * > ( new Handler1 ( ) ) ) ; scgi. addHandler ( "/xxx" , reinterpret_cast < IScgiHandler * > ( new Handler2 ( ) ) ) ; scgi. run ( ) ;
  7. // scgiServer scgi ; // pid_t pid ; if ( ( pid = scgi. demonize ( ) ) < 1 ) { if ( pid == - 1 ) { cerr << "demonize error \n " ; return 1 ; } return 0 ; } // if ( scgi. init ( "127.0.0.1" , 8080 ) ) { cerr << "server stopped \n " ; return 1 ; } // scgi. addHandler ( "/post" , reinterpret_cast < IScgiHandler * > ( new Handler1 ( ) ) ) ; scgi. addHandler ( "/xxx" , reinterpret_cast < IScgiHandler * > ( new Handler2 ( ) ) ) ; scgi. run ( ) ;
  8. // scgiServer scgi ; // pid_t pid ; if ( ( pid = scgi. demonize ( ) ) < 1 ) { if ( pid == - 1 ) { cerr << "demonize error \n " ; return 1 ; } return 0 ; } // if ( scgi. init ( "127.0.0.1" , 8080 ) ) { cerr << "server stopped \n " ; return 1 ; } // scgi. addHandler ( "/post" , reinterpret_cast < IScgiHandler * > ( new Handler1 ( ) ) ) ; scgi. addHandler ( "/xxx" , reinterpret_cast < IScgiHandler * > ( new Handler2 ( ) ) ) ; scgi. run ( ) ;
  9. // scgiServer scgi ; // pid_t pid ; if ( ( pid = scgi. demonize ( ) ) < 1 ) { if ( pid == - 1 ) { cerr << "demonize error \n " ; return 1 ; } return 0 ; } // if ( scgi. init ( "127.0.0.1" , 8080 ) ) { cerr << "server stopped \n " ; return 1 ; } // scgi. addHandler ( "/post" , reinterpret_cast < IScgiHandler * > ( new Handler1 ( ) ) ) ; scgi. addHandler ( "/xxx" , reinterpret_cast < IScgiHandler * > ( new Handler2 ( ) ) ) ; scgi. run ( ) ;
  10. // scgiServer scgi ; // pid_t pid ; if ( ( pid = scgi. demonize ( ) ) < 1 ) { if ( pid == - 1 ) { cerr << "demonize error \n " ; return 1 ; } return 0 ; } // if ( scgi. init ( "127.0.0.1" , 8080 ) ) { cerr << "server stopped \n " ; return 1 ; } // scgi. addHandler ( "/post" , reinterpret_cast < IScgiHandler * > ( new Handler1 ( ) ) ) ; scgi. addHandler ( "/xxx" , reinterpret_cast < IScgiHandler * > ( new Handler2 ( ) ) ) ; scgi. run ( ) ;
  11. // scgiServer scgi ; // pid_t pid ; if ( ( pid = scgi. demonize ( ) ) < 1 ) { if ( pid == - 1 ) { cerr << "demonize error \n " ; return 1 ; } return 0 ; } // if ( scgi. init ( "127.0.0.1" , 8080 ) ) { cerr << "server stopped \n " ; return 1 ; } // scgi. addHandler ( "/post" , reinterpret_cast < IScgiHandler * > ( new Handler1 ( ) ) ) ; scgi. addHandler ( "/xxx" , reinterpret_cast < IScgiHandler * > ( new Handler2 ( ) ) ) ; scgi. run ( ) ;
  12. // scgiServer scgi ; // pid_t pid ; if ( ( pid = scgi. demonize ( ) ) < 1 ) { if ( pid == - 1 ) { cerr << "demonize error \n " ; return 1 ; } return 0 ; } // if ( scgi. init ( "127.0.0.1" , 8080 ) ) { cerr << "server stopped \n " ; return 1 ; } // scgi. addHandler ( "/post" , reinterpret_cast < IScgiHandler * > ( new Handler1 ( ) ) ) ; scgi. addHandler ( "/xxx" , reinterpret_cast < IScgiHandler * > ( new Handler2 ( ) ) ) ; scgi. run ( ) ;
  13. // scgiServer scgi ; // pid_t pid ; if ( ( pid = scgi. demonize ( ) ) < 1 ) { if ( pid == - 1 ) { cerr << "demonize error \n " ; return 1 ; } return 0 ; } // if ( scgi. init ( "127.0.0.1" , 8080 ) ) { cerr << "server stopped \n " ; return 1 ; } // scgi. addHandler ( "/post" , reinterpret_cast < IScgiHandler * > ( new Handler1 ( ) ) ) ; scgi. addHandler ( "/xxx" , reinterpret_cast < IScgiHandler * > ( new Handler2 ( ) ) ) ; scgi. run ( ) ;
  14. // scgiServer scgi ; // pid_t pid ; if ( ( pid = scgi. demonize ( ) ) < 1 ) { if ( pid == - 1 ) { cerr << "demonize error \n " ; return 1 ; } return 0 ; } // if ( scgi. init ( "127.0.0.1" , 8080 ) ) { cerr << "server stopped \n " ; return 1 ; } // scgi. addHandler ( "/post" , reinterpret_cast < IScgiHandler * > ( new Handler1 ( ) ) ) ; scgi. addHandler ( "/xxx" , reinterpret_cast < IScgiHandler * > ( new Handler2 ( ) ) ) ; scgi. run ( ) ;
  15. // scgiServer scgi ; // pid_t pid ; if ( ( pid = scgi. demonize ( ) ) < 1 ) { if ( pid == - 1 ) { cerr << "demonize error \n " ; return 1 ; } return 0 ; } // if ( scgi. init ( "127.0.0.1" , 8080 ) ) { cerr << "server stopped \n " ; return 1 ; } // scgi. addHandler ( "/post" , reinterpret_cast < IScgiHandler * > ( new Handler1 ( ) ) ) ; scgi. addHandler ( "/xxx" , reinterpret_cast < IScgiHandler * > ( new Handler2 ( ) ) ) ; scgi. run ( ) ;
  16. // scgiServer scgi ; // pid_t pid ; if ( ( pid = scgi. demonize ( ) ) < 1 ) { if ( pid == - 1 ) { cerr << "demonize error \n " ; return 1 ; } return 0 ; } // if ( scgi. init ( "127.0.0.1" , 8080 ) ) { cerr << "server stopped \n " ; return 1 ; } // scgi. addHandler ( "/post" , reinterpret_cast < IScgiHandler * > ( new Handler1 ( ) ) ) ; scgi. addHandler ( "/xxx" , reinterpret_cast < IScgiHandler * > ( new Handler2 ( ) ) ) ; scgi. run ( ) ;
  17. // scgiServer scgi ; // pid_t pid ; if ( ( pid = scgi. demonize ( ) ) < 1 ) { if ( pid == - 1 ) { cerr << "demonize error \n " ; return 1 ; } return 0 ; } // if ( scgi. init ( "127.0.0.1" , 8080 ) ) { cerr << "server stopped \n " ; return 1 ; } // scgi. addHandler ( "/post" , reinterpret_cast < IScgiHandler * > ( new Handler1 ( ) ) ) ; scgi. addHandler ( "/xxx" , reinterpret_cast < IScgiHandler * > ( new Handler2 ( ) ) ) ; scgi. run ( ) ;
  18. // scgiServer scgi ; // pid_t pid ; if ( ( pid = scgi. demonize ( ) ) < 1 ) { if ( pid == - 1 ) { cerr << "demonize error \n " ; return 1 ; } return 0 ; } // if ( scgi. init ( "127.0.0.1" , 8080 ) ) { cerr << "server stopped \n " ; return 1 ; } // scgi. addHandler ( "/post" , reinterpret_cast < IScgiHandler * > ( new Handler1 ( ) ) ) ; scgi. addHandler ( "/xxx" , reinterpret_cast < IScgiHandler * > ( new Handler2 ( ) ) ) ; scgi. run ( ) ;
  19. // scgiServer scgi ; // pid_t pid ; if ( ( pid = scgi. demonize ( ) ) < 1 ) { if ( pid == - 1 ) { cerr << "demonize error \n " ; return 1 ; } return 0 ; } // if ( scgi. init ( "127.0.0.1" , 8080 ) ) { cerr << "server stopped \n " ; return 1 ; } // scgi. addHandler ( "/post" , reinterpret_cast < IScgiHandler * > ( new Handler1 ( ) ) ) ; scgi. addHandler ( "/xxx" , reinterpret_cast < IScgiHandler * > ( new Handler2 ( ) ) ) ; scgi. run ( ) ;
  20. // scgiServer scgi ; // pid_t pid ; if ( ( pid = scgi. demonize ( ) ) < 1 ) { if ( pid == - 1 ) { cerr << "demonize error \n " ; return 1 ; } return 0 ; } // if ( scgi. init ( "127.0.0.1" , 8080 ) ) { cerr << "server stopped \n " ; return 1 ; } // scgi. addHandler ( "/post" , reinterpret_cast < IScgiHandler * > ( new Handler1 ( ) ) ) ; scgi. addHandler ( "/xxx" , reinterpret_cast < IScgiHandler * > ( new Handler2 ( ) ) ) ; scgi. run ( ) ;


Installation

The library is implemented as static.
make lib - compile library
make compile example file

Compile your application:
cp libscgi.a / usr / local / lib
g ++ example.cpp -o scgi_server -Wall -g -L. -L / usr / local / lib -levent -lscgi

Limitations:

The length of the POST data and the response should be no more than 2K. This limitation can be changed by setting the corresponding value to the constant #define BUFFSIZE and rebuilding the library. For my tasks, this is enough.
The library is experimental, there are shortcomings and there may be shortcomings. Constructive criticism is welcome. All that is planned in TODO. Those who wish to test and help the development are always happy.

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


All Articles