C/C++教程

Fcgi框架解析

本文主要是介绍Fcgi框架解析,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

Fcgi框架解析

客户的请求到达WebServer后,会转向fcgi程序,fcgi解释器会循环等待新的连接到来:
在这里插入图片描述
FCGX_Accept_r 会在OS_Accpet()函数处堵塞,等待连接的到来,当新的连接到来时,会将request->ipcFd设置为对应的fd

在这里插入图片描述
这里的listen_sock为0,即标准输入,fcgi解释器会通过监听标准输入来等待新连接的到来。

当有新的连接到来时,在此程序中第一个可用的fd是3。返回后会设置输入流以读取数据。
在这里插入图片描述
NewReader创建新的FCGX_Stream结构体,地址保存在reqDataPtr->in中,FillBuffProc函数来真正读取数据,读出的数据格式如下
在这里插入图片描述
【23】之前的为FCGI协议的header部分,后面为数据部分,每一个消息前面都会有一个header,
header的结构体如下:

typedef struct
{
    unsigned char version;     //FCGI版本信息,目前一般定义为1
    unsigned char type;        //每次发送的消息的类型.相当于flag,此例子中的1表示一次请求的开始
    
    unsigned char requestIdB1; //合起来表示本次请求的编号 ID
    unsigned char requestIdB0;
    
    unsigned char contentLengthB1; //合起来表示 body 长度
    unsigned char contentLengthB0;
    
    unsigned char paddingLength; //填充字节长度,填充长度不可超过255字节
    unsigned char reserved;      //保留字节
} FCGI_Header;                   //消息头

其中type的字段的定义为:

// FCGI_Header 中 type 的具体值
#define FCGI_BEGIN_REQUEST 1  //一次请求的开始(web->fastcgi)
#define FCGI_ABORT_REQUEST 2  //异常终止一次请求(web->fastcgi)
#define FCGI_END_REQUEST   3  //请求处理完毕,正常结束(fastcgi->web)

#define FCGI_PARAMS        4  /*传递参数,表明消息中包含的数据为某个name-value对
(web->fastcgi)*/

#define FCGI_STDIN         5  
/*POST 内容传递,从浏览器接收到的POST请求数据(表单提交等)以消息的形式发给php-fpm时,
这种消息的type就得设为5(web->fastcgi)*/

#define FCGI_STDOUT        6 
//正常响应内容,php-fpm给web服务器回的正常响应消息的type就设为6(fastcgi->web)

#define FCGI_STDERR        7   
//php-fpm给web服务器回的错误响应设为7(fastcgi->web)

#define FCGI_DATA          8   //向CGI程序传递的额外数据(WEB->FastCGI) 
#define FCGI_GET_VALUES    9   // 向FastCGI程序询问一些环境变量(WEB->FastCGI)
#define FCGI_GET_VALUES_RESULT 10 // 询问环境变量的结果(FastCGI->WEB)
#define FCGI_UNKNOWN_TYPE      11 //通知 webserver 所请求 type 非正常类型
#define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE)  // 未知类型,可能用作拓展

当header中的type == 1时,接下来的8字节传递的是下面的消息体

typedef struct
{
    unsigned char roleB1; 
    unsigned char roleB0;
    //合起来表示 webserver 所期望php-fpm 扮演的角色,具体取值下面有


    unsigned char flags; //确定 php-fpm 处理完一次请求之后是否关闭,flag=1,不关闭
    unsigned char reserved[5]; //保留字段
} FCGI_BeginRequestBody;       //开始请求体
//webserver 期望 fcgi 扮演的角色(想让fcgi做什么)
#define FCGI_RESPONDER 1  
//接受http关联的所有信息,并产生http响应,接受来自webserver的PARAMS环境变量
#define FCGI_AUTHORIZER 2 
//对于认证的会关联其http请求,未认证的则关闭请求
#define FCGI_FILTER 3     
//过滤web server 中的额外数据流,并产生过滤后的http响应

回到前面的例子中,前16个字节中,header的type为1,代表一次请求的开始,后面的header中的type为4,表示消息的数据流,此部分为HTTP请求的QUERY_STRING, HTTP_HEADER等字段,主要格式为nameLen, valueLen, name[nameLen], value[valueLen]即前两个字节分别表示name和value的长度,接下来是name和value本身。
接下来fcgi调用ReadParams将数据流的字段解析到reqDataPtr->paramsPtr,这主要是一个指针数组,没个指针指向的都是从数据流中解析出的key=value形式的字符串。
接下来构造了两个对象:

cgicc::FCgiIO fCgiIO(request);
Cgicc cgicc(&fCgiIO);

FCgiIO类成员有:

class CGICC_API FCgiIO : public cgicc::CgiInput, public std::ostream
  {
  public:
  	...
protected:
    FCGX_Request& 			fRequest;
    fcgi_streambuf 			fOutBuf;
    fcgi_streambuf 			fErrBuf;
    std::ostream 			fErr;
    std::map<std::string, std::string> 	fEnv;
  };

FCgiIO类继承自CgiInputstd::ostream,其构造函数主要是将成员变量的输出流和错误流设置为request中的输出流和错误流,并且解析request中的kv格式的串,放到fEnv
Cgicc这个类是cgicc框架的主要部分,其类之间的继承关系架构图为
在这里插入图片描述
在Cgicc的构造函数中,首先初始化fEnvironment,其成员为

class fEnvironment {
	...
    unsigned long 		fServerPort;
    unsigned long 		fContentLength;
    bool			fUsingHTTPS;
    std::string 		fServerSoftware;
    std::string 		fServerName;
    std::string 		fGatewayInterface;
    std::string 		fServerProtocol;
    std::string 		fRequestMethod;
    std::string 		fPathInfo;
    std::string 		fPathTranslated;
    std::string 		fScriptName;
    std::string 		fQueryString;
    std::string 		fRemoteHost;
    std::string 		fRemoteAddr;
    std::string 		fAuthType;
    std::string 		fRemoteUser;
    std::string 		fRemoteIdent;
    std::string 		fContentType;
    std::string 		fAccept;
    std::string 		fUserAgent;
    std::string 		fPostData;
    std::string 		fRedirectRequest;
    std::string 		fRedirectURL;
    std::string 		fRedirectStatus;
    std::string 		fReferrer;
    std::string 		fCookie;
    std::vector<HTTPCookie> 	fCookies;
    std::string                 fAcceptLanguageString; 
  };
};

主要保存了HTTP请求的环境变量和主要的get,post数据。值得一提的是,在解析fPostData时,fEnvironment调用了FCgiIO.read()函数,这个函数主要调用FCGX_GetStr(data, length, fRequest.in);来继续获取输入流fRequest.in中的数据。
至此FCGI的输入部分就此结束。

类FastCGI解析

主要的成员变量:

class FastCGI
{
public:
	...
  void Run(FCGX_Request& request);
  void FetchHttpRequest(cgicc::FCgiIO *pCgiIO, cgicc::Cgicc* pCgicc, CReqData* reqData)
protected:  
    CReqData reqData;  //输入       
    CResData resData;//输出
    string tid;
    int outPutType; ///< 输出类型, 默认xml
    string subModule; ///< 配置结点名,默认 weixin

class CReqData : public CTransData {	//CTransData 只是指定了三个方法 getPara setPara,getMap
...
private:
	CStr2Map m_cookieMap;
	CStr2Map m_formDataMap ; //保存前台提交的数据
	CStr2Map m_formUrlDataMap;   //在有POST提交的情况, 保存前台提交GET方式的数据  
	CStr2Map m_envMap  ; //保存环境变量 Map 
	CStr2Map m_tempMap ;	//保存临时数据
	vector<cgicc::FormFile>  m_upLoadFileList ;		//保存上传的文件属性列表
    string m_strPostData;	    //post data
    string m_strQueryData;     //get data
};

main函数中,当接受到Request时,首先通过函数FetchHttpRequest(cgicc::FCgiIO *pCgiIO, cgicc::Cgicc* pCgicc, CReqData* reqData),填充ReqData,然后初始化ResData。
FetchHttpRequest里面关键的步骤主要是填充ReqData这个类:

  reqData->SetPostData(env);
  reqData->SetQueryData(env);
  reqData->SetEnv("ClientIp", "ClientIP");
  reqData->SetEnv("ClientAgent", env.getUserAgent());
  reqData->SetEnv("RequestMethod", env.getRequestMethod());
  reqData->SetEnv("CgiName", env.getScriptName());
  reqData->SetEnv("referer", env.getReferrer());
  reqData->SetEnv("Authorization", pCgiIO->getenv("HTTP_AUTHORIZATION"));
  //reqData->SetEnv("ServerIp", env.getServerName().empty()? "" : env.getServerName());
  char szIp[30] = {0};
  snprintf(szIp, sizeof(szIp), "%s", "1.3.4.5");
  reqData->SetEnv("ServerIp", szIp);

获取到ReqData后,输入部分的处理就到此结束了。

输出部分

输出部分主要是由CCgiOutput类处理。

class CCgiOutput
{
public:
    CCgiOutput(CResData* pResData, cgicc::FCgiIO* pOut):m_pResData(pResData), m_pOut(pOut){};
    virtual ~CCgiOutput(){} ;
    void PrintOutput(CReqData* pReqData);
protected:
    virtual void OutPutExcpion(CReqData* pReqData);
    void OutPut() ;
    void OutPutHtml();
    void FilterData();
    void SetHttpHeader();
    string EncodeJsonObject(const string& JsonObj);
    virtual void PrintJsonElem(const string& name ,const string& value) ;
    void OutPutJson(int iType=0) ;
    //不需要框架进行自动URL转码
    void PrintJsonElemNotEscape(const string& name ,const string& value);
    void OutPutJsonNotEscape( int iType=0 );
    void PrintXmlElement(const string & sName,const string& sValue) ;
    void OutPutXml() ;
	void OutPutXmlMySelf();
    stringstream m_strsteam;
private:
    //cgi输出
    CResData *m_pResData;
    //输出句柄
    cgicc::FCgiIO* m_pOut;
};

这个类的处理逻辑很简单,依据resData中指明的输出类型,调用不同的输出函数生成数据,例如输出的类型是OUTJSON,则调用OutPutJson();这个函数会以json的格式将resData中保存的数据转换为json字符串,保存到stringstream中,

这篇关于Fcgi框架解析的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!