This article introduces to you the content of the PHP source code in the FASTCGI protocol analysis, has a certain reference value, the need for friends can refer to.
FastCGI is a protocol, it is based on the cgi/1.1 Foundation, the cgi/1.1 inside the data to be passed through the FastCGI protocol definition of the order and format to pass. In order to better understand the work of PHP-FPM, the following concrete elaboration of the contents of the FASTCGI agreement.
1. Message type
The FASTCGI protocol is divided into 10 types, specifically defined as follows:
typedef enum _FCGI_REQUEST_TYPE { fcgi_begin_request = 1,/* [in] */ fcgi_abort_request = 2, /* [ in] (not supported) */ fcgi_end_request = 3,/* [out] */ fcgi_params = 4,/* [in]< C14/>environment variables */ Fcgi_stdin = 5,/* [in] post data */ Fcgi_stdout = 6,/* [out] response */ Fcgi_stderr = 7,/* [out] errors */ Fcgi_data = 8,/* [in] filter data (not supported) */ fcgi_get_values = 9,/* [in] * / Fcgi_get_values_result = ten/ * [out] */} Fcgi_request_type;
The whole fastcgi is binary continuous transmission, defines a unified structure of the message header, used to read each message body, convenient message packet cutting. In general, the Fcgi_begin_request type of message is sent first, followed by messages of type Fcgi_params and Fcgi_stdin, and Fcgi_stdout and Fcgi_ are sent when the fastcgi response is finished processing The stderr type of message, and finally fcgi_end_request to indicate the end of the request. Fcgi_begin_request and Fcgi_end_request represent the start and end of the request, respectively, related to the entire protocol.
2. Message headers
For 10 types of messages, starting with a message header, the structure is defined as follows:
typedef struct _FCGI_HEADER { unsigned char version; unsigned char type; unsigned char requestIdB1; unsigned char requestIdB0; unsigned char contentLengthB1; unsigned char contentLengthB0; unsigned char paddinglength; unsigned char reserved;} Fcgi_header;
which
Version Identity fastcgi protocol versions
Type identifies fastcgi record types
RequestID identifies the fastcgi request to which the message belongs
The RequestID is calculated as follows:
(requestIdB1 << 8) + requestIdB0
So the range of RequestID is 0~2 16-1, which is 0~65535;
The ContentLength identifies the number of bytes of the Contentdata component of the message, which is calculated in the same way as RequestID, and the range is 0~65535:
(contentLengthB1 << 8) | ContentLengthB0
The Paddinglength identifies the number of bytes of the Paddingdata component of the message, the range is 0~255, and the protocol provides the sender with the ability to populate the sent records by Paddingdata. and convenient to the recipient through the paddinglength to quickly skip the paddingdata. The purpose of padding is to allow the sender to handle the data that remains aligned more efficiently. What if the length of the content exceeds 65535? The answer is that it can be sent into multiple messages.
3. Fcgi_begin_request
The structure of the fcgi_begin_request is defined as follows:
typedef struct _FCGI_BEGIN_REQUEST { unsigned char roleB1; unsigned char roleB0; unsigned char flags; unsigned char reserved[5];} Fcgi_begin_request;
Where role represents the role that the Web server expects the application to play, the calculation is:
(roleB1 << 8) + roleB0
For PHP7, three roles were processed, namely Fcgi_responder,fcgi_authorizer and Fcgi_filter.
Flags & Fcgi_keep_conn: If 0, the link is closed after the response to this request. If not 0, the link is not closed after responding to this request.
4. Name-value pairs
For the type Fcgi_params, the FASTCGI protocol provides a name-value pair that is good for both read-write variable-length name and value, in the following format:
Namelength+valuelength+name+value
In order to save space, for 0~127 length value, length uses a char to represent, the first bit is 0, for a value greater than 127, length uses 4 char to represent, the first bit is 1;
The length calculation code is as follows:
if (Unexpected (Name_len >=)) { if (unexpected (p + 3 >= end)) return 0; Name_len = ((Name_len & 0x7f) <<); Name_len |= (*p++ <<); Name_len |= (*p++ << 8); Name_len |= *p++;}
This can express the length of the 0~2 31 times.
5. Request Agreement
The definition structure of the FASTCGI protocol is as follows:
typedef struct _FCGI_BEGIN_REQUEST_REC { fcgi_header hdr; Fcgi_begin_request body;} Fcgi_begin_request_rec;
After analyzing the FASTCGI protocol, we master the content of the requested fastcgi message, and we use GDB to fetch the content by accessing the corresponding interface:
First we modify the parameters of the php-fpm.conf to ensure that only one worker is started:
Pm.max_children = 1
Then restart PHP-FPM:
./sbin/php-fpm-y etc/php-fpm.conf
The worker is then GDB:
PS aux | grep php-fpmroot 30014 0.0 0.0 142308 4724? Ss Nov26 0:03 php-fpm:master process (etc/php-fpm.conf) chenlei 30015 0.0 0.0 142508 5500? S Nov26 0:00 php-fpm:pool wwwgdb–p 30015 (gdb) b fcgi_read_request
The contents of the FASTCGI message are then printed out by GDB, which is then forwarded to the PHP-FPM worker via the browser access Nginx,nginx:
(GDB) B fcgi_read_request
For the first message, the content
Where type corresponds to Fcgi_begin_request,requestid is 1, the length is 8, exactly is the size of the fcgi_begin_request structure, the content
Role corresponds to Fcgi_responder. Continue reading, get the message content
Where type corresponds to Fcgi_params,requestid is 1, length is:
(contentLengthB1 << 8) | contentLengthB0 = = 987
Paddinglength=5, while 987+5=992, happens to be multiples of 8. Read the 992-length byte stream backwards according to contentlength+ Paddinglength, and we print:
(GDB) P *p@987$1 = "\017tscript_filename/home/xiaoju/webroot/beatles/application/mis/mis/src/index.php/admin/ Operation/index\f\016query_stringactivity_id=89\016\003request_methodget\f\000content_type\016\000content_ Length\v script_name/index.php/admin/operation/index\v%request_uri/admin/operation/index?activity_id=89\f document_uri/index.php/admin/operation/index\r4document_root/home/xiaoju/webroot/beatles/application/mis/mis/ Src\017\bserver_protocolhttp/1.1\021\agateway_interfacecgi/1.1\017\vserver_softwarenginx/1.2.5\v\rremote_ Addr172.22.32.131\v\005remote_port50973\v\fserver_addr10.94.98.116\v\004server_port8085\v\000server_name\017\ 003redirect_status200\t\021http_host10.94.98.116:8085\017\nhttp_connectionkeep-alive\017xhttp_user_ agentmozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) applewebkit/537.36 (khtml, like Gecko) chrome/62.0.3202.94 Safari/537.36\036\001http_upgrade_ insecure_requests1\vuhttp_accepttext/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/ *; Q=0.8\024\rhttp_accept_encodinggzip, deflate\024\027http_accept_languagezh-cn,zh;q=0.9,en;q=0.8 "
According to the length rule of the name-value pair we talked about in the previous section, we can see that the FASTCGI protocol encapsulates a key-value pair similar to the one in the HTTP protocol. After reading, continue to trace the message, print can draw, get the message.
Where type corresponds to a fcgi_params,requestid of 1 and a length of 0, this completes the FASTCGI protocol message reading process. The following is a message that returns to the Nginx fastcgi protocol after processing the request.
6. Response Agreement
In Fcgi_finish_request, call Fcgi_flush,fcgi_flush to encapsulate a fcgi_end_request message body, and then write the client descriptor of the socket connection by Safe_write.
int Fcgi_flush (fcgi_request *req, int close) {int len; Close_packet (req); len = (int) (REQ->OUT_POS-REQ->OUT_BUF); if (Close) {Fcgi_end_request_rec *rec = (fcgi_end_request_rec*) (Req->out_pos); Create Fcgi_end_request's head fcgi_make_header (&REC->HDR, fcgi_end_request, Req->id, sizeof (fcgi_end_requ EST)); Write Appstatus rec->body.appstatusb3 = 0; REC->BODY.APPSTATUSB2 = 0; REC->BODY.APPSTATUSB1 = 0; rec->body.appstatusb0 = 0; Modify Protocolstatus to Fcgi_request_complete; Rec->body.protocolstatus = Fcgi_request_complete; Len + sizeof (FCGI_END_REQUEST_REC); } if (Safe_write (req, req->out_buf, len)! = len) {req->keep = 0; Req->out_pos = req->out_buf; return 0; } Req->out_pos = req->out_buf; return 1;}
The
Here we have a complete grasp of the fastcgi agreement.