搜索
首页后端开发php教程php源码中FastCGI协议的解析

php源码中FastCGI协议的解析

Jul 20, 2018 am 10:07 AM
php

这篇文章给大家介绍的内容是关于php源码中FastCGI协议的解析,有着一定的参考价值,有需要的朋友可以参考一下。

FastCGI 是一种协议,它是建立在CGI/1.1基础之上的,把CGI/1.1里面的要传递的数据通过FastCGI协议定义的顺序和格式进行传递。为了更好理解PHP-FPM的工作,下面具体阐述一下FastCGI协议的内容。

1. 消息类型

FastCGI协议分为了10种类型,具体定义如下:

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]  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 = 10  /* [out] */

} fcgi_request_type;

整个FastCGI是二进制连续传递的,定义了一个统一结构的消息头,用来读取每个消息的消息体,方便消息包的切割。一般情况下,最先发送的是FCGI_BEGIN_REQUEST类型的消息,然后是FCGI_PARAMS和FCGI_STDIN类型的消息,当FastCGI响应处理完后,将发送FCGI_STDOUT和FCGI_STDERR类型的消息,最后以FCGI_END_REQUEST表示请求的结束。FCGI_BEGIN_REQUEST和FCGI_END_REQUEST分别表示请求的开始和结束,与整个协议相关。

2. 消息头

对于10种类型的消息,都是以一个消息头开始的,其结构体定义如下:

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;

其中,

  • version标识FastCGI协议版本

  • type 标识FastCGI记录类型

  • requestId标识消息所属的FastCGI请求

requestId计算方式如下:

(requestIdB1 << 8) + requestIdB0

所以requestId的范围为0~2的16次方-1,也就是0~65535;

contentLength标识消息的contentData组件的字节数,计算方式跟requestId类似,范围同样是0~65535:

(contentLengthB1 << 8) | contentLengthB0

paddingLength标识消息的paddingData组件的字节数,范围是0~255;协议通过paddingData提供给发送者填充发送的记录的功能,并且方便接受者通过paddingLength快速的跳过paddingData。填充的目的是允许发送者为更有效地处理保持对齐的数据。如果内容的长度超过65535怎么办呢?答案是可以分成多个消息发送。

3. FCGI_BEGIN_REQUEST

FCGI_BEGIN_REQUEST 的结构体定义如下:

typedef struct _fcgi_begin_request {

      unsigned char roleB1;

      unsigned char roleB0;

      unsigned char flags;

      unsigned char reserved[5];

} fcgi_begin_request;

其中role代表的是Web服务器期望应用扮演的角色,计算方式是:

(roleB1 << 8) + roleB0

对于PHP7中,处理了三种角色,分别是FCGI_RESPONDER,FCGI_AUTHORIZER 和FCGI_FILTER。

flags & FCGI_KEEP_CONN:如果为0,则在对本次请求响应后关闭链接。如果非0,在对本次请求响应后不会关闭链接。

4. 名-值对

对于,type为FCGI_PARAMS类型,FastCGI协议中提供了名-值对来很好的满足读写可变长度的name和value,格式如下:

nameLength+valueLength+name+value

为了节省空间,对于0~127长度的值,Length使用了一个char来表示,第一位为0,对于大于127的长度的值,Length使用了4个char来表示,第一位为1;如图所示clipboard.png

长度计算代码如下:

if (UNEXPECTED(name_len >= 128)) {

      if (UNEXPECTED(p + 3 >= end)) return 0;

      name_len = ((name_len & 0x7f) << 24);

      name_len |= (*p++ << 16);

      name_len |= (*p++ << 8);

      name_len |= *p++;

}

这样最长可以表达0~2的31次方的长度。

5. 请求协议

FastCGI协议的定义结构体如下:

    typedef struct _fcgi_begin_request_rec {

      fcgi_header hdr;

      fcgi_begin_request body;

} fcgi_begin_request_rec;

分析完FastCGI的协议,我们整体掌握了请求的FastCGI消息的内容,我们通过访问对应的接口,采用gdb抓取其中的内容:

首先我们修改php-fpm.conf的参数,保证只启动一个worker:

pm.max_children = 1

然后重新启动php-fpm:

./sbin/php-fpm -y etc/php-fpm.conf

然后对worker进行gdb:

ps aux | grep php-fpm

root     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 www

gdb –p 30015

(gdb) b fcgi_read_request

然后通过浏览器访问nginx,nginx转发到php-fpm的worker上,根据gdb可以打印出FastCGI消息的内容:

(gdb) b fcgi_read_request

对于第一个消息,内容如图:

clipboard.png

其中type对应的是FCGI_BEGIN_REQUEST,requestid为1,长度为8, 恰好是fcgi_begin_request结构体的大小,内容如图:

clipboard.png

role对应的是FCGI_RESPONDER。继续往下读,得到的消息内容如图:

clipboard.png

其中type对应的是FCGI_PARAMS,requestid为1,长度为:

(contentLengthB1 << 8) | contentLengthB0  == 987

paddingLength=5,而987+5=992,恰好是8的倍数。根据contentLength+ paddingLength向后读取992长度的字节流,我们打印一下:

(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"

根据上一节我们讲到的名-值对的长度规则,我们可以看出,Fastcgi协议中封装了类似于http协议里面的键值对。读取完毕后,继续跟踪消息,打印可以得出,得到的消息如图所示。

clipboard.png

其中type对应的是FCGI_PARAMS,requestid为1,长度为0,此时完成了FastCGI协议消息的读取过程。下面说一下处理完请求后返回给nginx的FastCGI协议的消息。

6. 响应协议

在fcgi_finish_request中调用fcgi_flush,fcgi_flush中封装一个FCGI_END_REQUEST消息体,再通过safe_write写入 socket 连接的客户端描述符。

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);

                 //创建FCGI_END_REQUEST的头

               fcgi_make_header(&rec->hdr, FCGI_END_REQUEST, req->id, sizeof(fcgi_end_request));

                 //写入appStatus

               rec->body.appStatusB3 = 0;

               rec->body.appStatusB2 = 0;

               rec->body.appStatusB1 = 0;

               rec->body.appStatusB0 = 0;

                 //修改protocolStatus为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;

}

到此我们就完全掌握了FastCGI的协议。

相关推荐:

PHP开发中redis的主从模式以及加密方法

以上是php源码中FastCGI协议的解析的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
您如何修改PHP会话中存储的数据?您如何修改PHP会话中存储的数据?Apr 27, 2025 am 12:23 AM

tomodifyDataNaphPsession,startTheSessionWithSession_start(),然后使用$ _sessionToset,修改,orremovevariables.1)startThesession.2)setthesession.2)使用$ _session.3)setormodifysessessvariables.3)emovervariableswithunset()

举一个在PHP会话中存储数组的示例。举一个在PHP会话中存储数组的示例。Apr 27, 2025 am 12:20 AM

在PHP会话中可以存储数组。1.启动会话,使用session_start()。2.创建数组并存储在$_SESSION中。3.通过$_SESSION检索数组。4.优化会话数据以提升性能。

垃圾收集如何用于PHP会议?垃圾收集如何用于PHP会议?Apr 27, 2025 am 12:19 AM

PHP会话垃圾回收通过概率机制触发,清理过期会话数据。1)配置文件中设置触发概率和会话生命周期;2)可使用cron任务优化高负载应用;3)需平衡垃圾回收频率与性能,避免数据丢失。

如何在PHP中跟踪会话活动?如何在PHP中跟踪会话活动?Apr 27, 2025 am 12:10 AM

PHP中追踪用户会话活动通过会话管理实现。1)使用session_start()启动会话。2)通过$_SESSION数组存储和访问数据。3)调用session_destroy()结束会话。会话追踪用于用户行为分析、安全监控和性能优化。

如何使用数据库存储PHP会话数据?如何使用数据库存储PHP会话数据?Apr 27, 2025 am 12:02 AM

利用数据库存储PHP会话数据可以提高性能和可扩展性。1)配置MySQL存储会话数据:在php.ini或PHP代码中设置会话处理器。2)实现自定义会话处理器:定义open、close、read、write等函数与数据库交互。3)优化和最佳实践:使用索引、缓存、数据压缩和分布式存储来提升性能。

简单地说明PHP会话的概念。简单地说明PHP会话的概念。Apr 26, 2025 am 12:09 AM

phpsessionstrackuserdataacrossmultiplepagerequestsusingauniqueIdStoredInacookie.here'showtomanageThemeffectionaly:1)startAsessionWithSessionwwithSession_start()和stordoredAtain $ _session.2)

您如何循环中存储在PHP会话中的所有值?您如何循环中存储在PHP会话中的所有值?Apr 26, 2025 am 12:06 AM

在PHP中,遍历会话数据可以通过以下步骤实现:1.使用session_start()启动会话。2.通过foreach循环遍历$_SESSION数组中的所有键值对。3.处理复杂数据结构时,使用is_array()或is_object()函数,并用print_r()输出详细信息。4.优化遍历时,可采用分页处理,避免一次性处理大量数据。这将帮助你在实际项目中更有效地管理和使用PHP会话数据。

说明如何使用会话进行用户身份验证。说明如何使用会话进行用户身份验证。Apr 26, 2025 am 12:04 AM

会话通过服务器端的状态管理机制实现用户认证。1)会话创建并生成唯一ID,2)ID通过cookies传递,3)服务器存储并通过ID访问会话数据,4)实现用户认证和状态管理,提升应用安全性和用户体验。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一个PHP/MySQL的Web应用程序,非常容易受到攻击。它的主要目标是成为安全专业人员在合法环境中测试自己的技能和工具的辅助工具,帮助Web开发人员更好地理解保护Web应用程序的过程,并帮助教师/学生在课堂环境中教授/学习Web应用程序安全。DVWA的目标是通过简单直接的界面练习一些最常见的Web漏洞,难度各不相同。请注意,该软件中

EditPlus 中文破解版

EditPlus 中文破解版

体积小,语法高亮,不支持代码提示功能

SecLists

SecLists

SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

WebStorm Mac版

WebStorm Mac版

好用的JavaScript开发工具