• cgi 操作封装


    #ifndef _CGI_HELPER_2005_06_06
    #define _CGI_HELPER_2005_06_06

    //////////////////////////////////
    //module name: cgi 操作封装
    //author:      wwjs(华有为)
    //create date: 2005-06-06
    //modify date: 2005-06-06
    //modify log:
    //
    ////////////////////////////////
    #include <stdlib.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <time.h>
    #include <map>
    #include <string>
    #include <vector>
    using namespace std;


    typedef struct tagCGIFORMDATA
    {
    vector<unsigned char> vData;
    long lType;
    string szName;
    string szExtend;
    tagCGIFORMDATA()
       :lType(0)
    {
    }
    }tagCGIFORMDATA;

    typedef map<string, string> CGIPARAMS;
    typedef map<string, tagCGIFORMDATA> CGIFROMDATAS ;


    /*
    函数介绍:去掉字符串2头的空格
    输入参数:需要去掉空格的字符串szSource
    输出参数:已经去掉空格后字符串szDest
    返回值:已经去掉空格后字符串的个数
    */
    inline int TrimString(string& szDest, string szSource)
    {
    szDest = "";
    size_t i = 0;
        for(; i < szSource.size() && szSource[i] == ' '; i++);//去掉前面空格
    size_t j = szSource.size();
    if(j > 0)
    {
       for(; j > 0 && szSource[i] == ' '; j--);//去掉后面空格
    }

    if(j > i)
       szDest = szSource.substr(i, j-i);//取出去掉空格后的字符串

    return szDest.size();
    }

    /*
    函数介绍:将URL路径字符串用分割字符分成一段段,在把等号两旁的字符串分别放到两个临时的string,
         成对应关系保存到map模版类中
    输入参数:需要拆分的字符串,分割字符
    输出参数:处理过的CGIPARAMS
    返回值:模版类map的大小
    */
    inline int GetCGIParams(CGIPARAMS& cgiParam, const string& szData, const char* lpszFind)
    {
    cgiParam.clear();

    string szParam;
    string szValue;
    int pos1 = 0;
    while(true)
    {
       int pos2 = szData.find('=', pos1);
       if(pos2 <= 0)
        break;
           
       TrimString(szParam, szData.substr(pos1, pos2-pos1));//把szData字符串中等号前的字符给szParam
           
       int pos3 = szData.find(lpszFind, pos2);
       if(pos3 <= 0)
       {
        TrimString(szValue, szData.substr(pos2+1, szData.size() - pos2-1));
           
        cgiParam[szParam] = szValue;
        break;//到字符串末尾,跳出死循环
       }

       TrimString(szValue, szData.substr(pos2+1, pos3 - pos2-1));//把szData字符串中等号到lpszFind之间的字符给szValue
       cgiParam[szParam] = szValue;

       pos1=pos3+1;
    }
    return cgiParam.size();
    }

    /*
    函数介绍:进行URL解码
    输入参数:需要拆分的字符串,分割字符
    输出参数:处理过的CGIPARAMS
    返回值:模版类map的大小
    */
    inline int GetCGIParams(CGIPARAMS& cgiParam)
    {
    cgiParam.clear();

    string szQueryString;
    #ifdef _DEBUG
    szQueryString = "http://wwjs-pc/stat/qbox_stat.htm?ticket=38E363F6963BF58AED42812B1D459F8DE9CBC4BA1773DA4A882F4236BA2254EF49AB7427C62609B3844FE0C1B03D7CC427A391032C97E84F629A0374F5E12255C1943D7CBD9DD77F07C8C7222DBD4BA2D50B39244C58B08C71280BE77F0B42616987B840F6894FD2C55E6956EC359FBF5B5D1B18056A6C125EA28F1C4E9BB2431AC453559E7A293A8CB7CF7E5C37774E2E18EE4997C54C1BB4CE72D95C2FB7CE&loginParam=FBAC30E4C6A582E3FC020D16C032F980AC665607F7D8D3280791BEE53E771864&length=26&sessionKey=38E363F6963BF58AB5EA3D516E985865";
    #else
    char* pData = getenv("QUERY_STRING");
    szQueryString = pData != 0 ? pData : "";//如果环境变量中有字符串则把它赋值给szQueryString,负责把空字符赋szQueryString
    #endif
    return GetCGIParams(cgiParam, szQueryString, "&");
    }

    /*
    函数介绍:进行Cookie解码
    输入参数:需要拆分的字符串,分割字符
    输出参数:处理过的CGIPARAMS
    返回值:模版类map的大小
    */
    inline int GetCookies(CGIPARAMS& cgiParam)
    {
    #ifdef _DEBUG
    string szCookies = "loginParam=FBAC30E4C6A582E3FC020D16C032F9803B92AC1C493167C125713FFF0F5D9087;length=26;sessionKey=38E363F6963BF58AB5EA3D516E985865";
    #else
    char* lpszCookies = getenv("HTTP_COOKIE");
    string szCookies = lpszCookies != 0 ? lpszCookies : "";
    #endif
    return GetCGIParams(cgiParam, szCookies, ";");
    }


    ////////////////// cgi form 处理部分

    class CCGIParamsEx
    {
    #define CGI_CRLF   "\r\n"
    #define HTTP_GET   "GET"
    #define HTTP_POST   "POST"
    #define X_CONTENT_TYPE "application/x-www-form-urlencoded"
    #define M_CONTENT_TYPE "multipart/form-data"

    unsigned char *HTTPBuffer;
    unsigned char *REQUEST_METHOD;
    unsigned char *CONTENT_TYPE;
    unsigned char *CONTENT_LENGTH;
    unsigned char *REMOTE_ADDR;
    unsigned long ulVersion;


    /***************queue start***************/
    typedef struct _UploadQueue_{
    unsigned char type;
    unsigned char name[255];
    unsigned char *value;
    unsigned char extend[255];
    unsigned long size;
    struct _UploadQueue_ *next;
    }UploadQueue, *PUploadQueue;

    typedef struct _QUEUEU_{

    int IsInit; //是否初始化
    unsigned int serial;   //队列元素个数
    /*
    函数介绍:输入队列函数
    输入参数:_UploadQueue_结构中的各参数,Q被定义成指针的指针,用来存放指向队列下个元素指针的地址
    输出参数:被填充后的_UploadQueue_结构
    返回值:
    */
    void push(PUploadQueue * Q,unsigned char type,unsigned char * name,unsigned char * value,unsigned char * extend, unsigned long size)
    {
       PUploadQueue tmp = (PUploadQueue)malloc(sizeof(UploadQueue));
       PUploadQueue PQ = *Q;
       if (tmp!=NULL)
       {
        tmp->type = type;
        strcpy((char*)tmp->name, (char*)name);
        tmp->value = (unsigned char *)malloc(size+1);
        memset(tmp->value, 0x0, size+1);
        memcpy(tmp->value, value, size);
        strcpy((char*)tmp->extend, (char*)extend);
        tmp->size = size;
        tmp->next = NULL;
        if (*Q==NULL)
        {
         *Q = tmp;
        }
        else
        {
         while(PQ!=NULL&&PQ->next!=NULL)//循环把指针指到队列的末尾,再往队列中插值
         {
          PQ = PQ->next;
         }
         PQ->next = tmp;
        }
       }
       else
       {
        //   printf("No memory available.\n");
       }
    }
    /*
    函数介绍:输出队列函数
    输入参数:指向_UploadQueue_结构指针的指针
    输出参数:指向队列下个元素
    返回值:
    */
    void pop(PUploadQueue *Q)
    {
       PUploadQueue tmp;
       if (IsEmpty(*Q)) return;//空队列,直接退出
       tmp = *Q;
       *Q = (*Q)->next;//首个元素弹出,指针后移
       free(tmp->value);
       free(tmp);
    }

    int IsEmpty(PUploadQueue Q)
    {
       return Q==NULL;
    }
    /*
    函数介绍:保存队列函数
    输入参数:待保存的_UploadQueue_结构
    输出参数:名称与tagCGIFORMDATA结构对应存入的cgiDatas
    返回值:
    */
    void SaveQueue(CGIFROMDATAS& cgiDatas, PUploadQueue Q)
    {
       char filePath[255]={0};
       FILE * fout = NULL;
       time_t timer = time(NULL);
       struct tm *now = localtime(&timer);
       char   psBuffer[128]={0}, szLog[256]={0};
       char * pszTmp = NULL;
       for(int n = 0; Q!=NULL; n++)//循环整个队列
       {
        if(Q->size > 0)
        {
         tagCGIFORMDATA d;
         d.lType = Q->type;//把_UploadQueue_结构中的数据存到tagCGIFORMDATA结构
         d.vData.resize(Q->size);
         d.szName = (char*)Q->name;
         d.szExtend = (char*)Q->extend;

         memcpy(&d.vData[0], Q->value, Q->size);
         cgiDatas[(char*)Q->name] = d;//名称与tagCGIFORMDATA结构对应存到map模版
        }
        Q = Q->next;
       }
    }
    }Queue, *PQueue;


    /***********http request start**********/
    typedef struct _HTTPREQUEST_{
    int IsInit;
    char *(*Request)(char *key, char *sOut);
    void (*FreeHTTPEnv)();
    }HTTPRequest, *PHTTPRequest;

    /*
    保存文件
    xPath = 路径
    xIn = 数据
    xl = 数据长度
    */


    Queue Q;
    PUploadQueue UQ;
    HTTPRequest R;

    /***************queue end***************/


    /***********http request end**********/

    /***********http upload start**********/
    typedef struct _CUPLOAD_{
    /*
    int (*GetBoundary)(char*);
    int (*GetFormType)(char *sIn);
    void (*GetFormName)(char *sIn, char *sOut);
    void (*GetFileExtendA)(char *sIn, char *sOut);
    void (*GetFileExtendB)(char *sIn, char *sOut);
    void (*DBParse)(unsigned char *sIn, unsigned long sPos, unsigned long sl);
    int (*UImport)(char* sIn, unsigned long sl, char* key);
    PUploadQueue (*UExport)(PUploadQueue *Q, char *name);
    */
    }CUpload, *PCUpload;

    CUpload U;

    /***********http upload end**********/
    /*
    函数介绍:字符转十六进制
    输入参数:需要转换的字符串sIn,转换个数sl
    输出参数:已经转换的字符串sOut
    返回值:
    */
    void BinToHex(unsigned char *sIn, char *sOut, int sl){
    int i;
    *sOut = 0x0;
    for (i=0;i<sl;i++)
    {
       sprintf(sOut+strlen(sOut), "%2.2X", sIn[i]);//把字符转换成宽度为2的十六进制存到sOut中
    }
    }
    /*
    函数介绍:在一个字符串中指定一段位置与另一个字符串比较
    输入参数:被比较的字符串,开始位置,结束位置,比较字符串
    输出参数:比较第一个相等字符在sIn中的位置
    返回值:比较第一个相等字符在sIn中的位置
    */
    unsigned long substring(int sPos, char *sIn, int sl, char *key){
    int i = 0;
    int kl = (int)strlen(key);
    for (i=sPos;i<sl;i++)
    {
       if (!strncmp(&sIn[i], key, kl))
       {//这样来找字符串也是没办法的事,挺浪费资源,strstr只能找无chr(0)的字符串,可能就是这里耽搁了上行时间,一个20MB的文件本地上传测试速度是3秒
         return i;
       }
    }
    return (unsigned long)-1;
    }


    void InitHTTPRequest(PHTTPRequest r)
    {
    InitHTTPEnv();
    r->IsInit = 1;
    r->Request = &Request;
    r->FreeHTTPEnv = &FreeHTTPEnv;


    InitQueue(&Q);


    InitCUpload(&U);
    }

    /*
    函数介绍:初始化HTTP环境变量
    输入参数:
    输出参数:
    返回值:
    */
    void InitHTTPEnv()
    {
    unsigned long cl = 0;
    REQUEST_METHOD = (unsigned char *)getenv("REQUEST_METHOD");
    CONTENT_TYPE = (unsigned char *)getenv("CONTENT_TYPE");
    CONTENT_LENGTH = (unsigned char *)getenv("CONTENT_LENGTH");
    if (CONTENT_LENGTH != NULL)
    {
       cl = (unsigned int)atoi((char*)CONTENT_LENGTH);//标准输入流中数据大小
       HTTPBuffer = (unsigned char *)malloc(cl + 1);
       memset((char *)HTTPBuffer, 0x0, cl + 1);//初始化内存区域,都赋值为零
    #ifdef WIN32
       _setmode(fileno(stdin), O_BINARY);
    #endif
       fread(HTTPBuffer, cl, 1, stdin);//把标准输入流中的数据读到HTTPBuffer中
    #ifdef WIN32
       _setmode(fileno(stdin), O_TEXT);
    #endif
    }
    }


    //释放malloc了的资源
    static void FreeHTTPEnv(){}

    static char *Request(char *key, char *sOut)
    {
    return sOut;
    }


    /*
    函数介绍:获取二进制分割符
    输入参数:
    输出参数:二进制分割符
    返回值:成功返回0,不成功则非0
    */
    int GetBoundary(char *boundary)
    {
    int ml = (int)strlen(M_CONTENT_TYPE);
    int cl;
    int i;
    int sPos = ml + 11;//11 byte length just '; boundary='
    *boundary = 0x0;
    if (CONTENT_LENGTH == NULL)
    {
       return 1;
    }
    if (CONTENT_TYPE == NULL)
    {
       return 2;
    }
    if (!strncmp((char*)CONTENT_TYPE, M_CONTENT_TYPE, ml))
    {
       *boundary++ = '-';
       *boundary++ = '-';
       cl = (int)strlen((char*)CONTENT_TYPE);
       for (i=sPos; i<cl; i++)
       {
        *boundary++ = CONTENT_TYPE[i];//不明白为什么往boundary填充"--"
       }
       *boundary = 0x0;
       return 0;
    }
    return 3;
    }


    /*
    函数介绍:获取表单类型
    输入参数:表单类型字符串
    输出参数:类型值
    返回值:0:表单域;1:文件域
    */
    int GetFormType(char *sIn)
    {
    char key[255]={0};
    char *p;
    sprintf(key, "filename=%c", 34);
    p = strstr(sIn, key);
    return p!=NULL;
    }

    /*
    函数介绍:获取表单名
    输入参数:表单类型字符串
    输出参数:表单名称字符串sOut
    返回值:
    */
    void GetFormName(char *sIn, char *sOut)
    {
    char key[255]={0};
    char *p;
    sprintf(key, "name=%c", 34);
    p = strstr(sIn, key);
    *sOut = 0x0;
    if (p)
    {
       p += (int)strlen(key);
       while(*p && (*p != '"'))
       {
        *sOut++ = *p++;
       }
    }
    }

    /*
    函数介绍:从文件类型获取文件扩展名,文件类型太多,使用的时候自己补充
    输入参数:待判断扩展名的文件名字符串
    输出参数:增加文件扩展名后的文件名字符串
    返回值:
    */
    void GetFileExtendA(char *sIn, char *sOut)
    {
    char buf[16]={0};
    BinToHex((unsigned char*)sIn, buf, 4);
    if (!strncmp(sIn, "MZ", 2))
    {
       strcpy(sOut, "exe");//exe程序文件
    }
    else if (!strncmp(sIn, "PK", 2))
    {
       strcpy(sOut, "zip");//zip文件
    }
    else if (!strncmp(sIn, "BM", 2))
    {
       strcpy(sOut, "bmp");//bmp文件
    }
    else if (!strncmp(sIn+1, "PDF", 3))
    {
       strcpy(sOut, "pdf");//pdf文件
    }
    else if (!strncmp(sIn, "Rar", 3))
    {
       strcpy(sOut, "rar");//rar文件
    }
    else if (!strncmp(sIn, "GIF", 3))
    {
       strcpy(sOut, "gif");//gif文件
    }
    else if (!strncmp(sIn+1, "PNG", 3))
    {
       strcpy(sOut, "png");//png文件
    }
    else if (!strncmp(sIn, "FWS", 3))
    {
       strcpy(sOut, "swf");//swf文件
    }
    else if (!strncmp(sIn, "CWS", 3))
    {
       strcpy(sOut, "swf");//flash swf文件
    }
    else if (!strncmp(sIn, "RIFF", 4))
    {
       strcpy(sOut, "avi");//avi文件
    }
    else if (!strncmp(buf, "1F8B", 4))
    {
       strcpy(sOut, "gz");//gzip件
    }
    else if (!strncmp(buf, "FFD8FFE0", 8))
    {
       strcpy(sOut, "jpg");//jpg文件
    }
    else if (!strncmp(buf, "D0CF11E0", 8))
    {
       strcpy(sOut, "doc");//doc文件,这里的格式判断我是用UltraEdit打开,查看它的十六进制格式所得的结果,其实有8个字节长度,内容为:D0CF11E0A1B11AEE。第9位起才是Chr(0)
    }
    else if (!strncmp(buf, "FFFB9404", 8))
    {
       strcpy(sOut, "mp3");//mp3文件
    }
    else if (!strncmp(buf, "XML", 8))
    {
       strcpy(sOut, "xml");//XML文件
    }
    else
    {
       *sOut = 0x0;
    }
    }

    /*
    函数介绍:从Content-Type获取文件名
    输入参数:待取出文件名的字符串
    输出参数:文件名字符串
    返回值:
    */
    void GetFileExtendB(char *sIn, char *sOut)
    {
    char *key = ".";
    char *p;
    p = strstr(sIn, key);
    *sOut = 0x0;
    if (p)
    {
       p += (int)strlen(key);
       while(*p && (*p != '"'))
       {
        *sOut++ = *p++;
       }
    }
    }

    /*
    函数介绍:解析数据,保存到队列
    输入参数:待解析的URL字符串sIn,第一个二进制分隔符位置加二进制分隔符长度加"/r/n/r/n"字符长度的值,
       两个二进制分割符中的数据
    输出参数:
    返回值:
    */
    void DBParse(unsigned char *sIn, unsigned long sPos, unsigned long sl)
    {
    unsigned char type = 0, name[255]={0}, *value, extend[255]={0};
    // unsigned long size = 0;
    int cl = (int)strlen(CGI_CRLF); //2
    unsigned long hl;//head length
    unsigned long vl;
    unsigned long dl;
    char *buf = (char *)malloc(sl+1);
    char *tmp;
    memset(buf, 0x0, sl+1);
    memcpy(buf, sIn + sPos, sl);//两个二进制分割符之间的字符考到buf
    hl = substring(0, buf, sl, "\r\n\r\n");//BUF中'\'的位置
    tmp = (char *)malloc(hl + 1);
    memset(tmp, 0x0, hl + 1);
    memcpy(tmp, buf, hl);//将BUF中末尾的"\r\n\r\n"字符串截断放到tmp中
    type = GetFormType(tmp);
    GetFormName(tmp, (char *)name);
    //fwrite(buf, sl, 1, stdout);
    dl = hl + cl + cl;
    vl = sl - dl;
    value = (unsigned char *)malloc(vl + 1);
    memset(value, 0x0, vl+1);
    memcpy(value, buf + dl, vl);
    if (type == 1 && vl > 0)
    {
       GetFileExtendA((char*)value, (char *)extend);
       //printf("Extend From Byte: %s\n", extend);
       if (extend[0] == 0x0)
       {
        GetFileExtendB(tmp, (char *)extend);
       }
    }
    if (Q.IsInit==1)
    {
       Q.push(&UQ, type, name, value, extend, vl);//将上载队列结构_UploadQueue_压入队列
       Q.serial++;   //队列元素个数累加
    }
    free(value);
    free(tmp);
    free(buf);
    }

    /*
    函数介绍:上传入口函数
    输入参数:sIn = stdin里面的数据
        sl = CONTENT_LENGTH
        key = 二进制分割符boundary
    输出参数:
    返回值:成功返回1
    */
    int UImport(char* sIn, unsigned long sl, char* key)
    {
    int kl = (int)strlen(key);
    int cl = (int)strlen(CGI_CRLF);//2
    unsigned long sPos, ePos;
    sPos = substring(0, sIn, sl, key);
    while (sPos != -1)
    {
       ePos = substring(sPos + kl, sIn, sl, key);//第二个二进制分隔符的起始位置
       if (ePos == -1)
       {
        break;
       }
       //printf("sPos: %d ePos: %d ePos - sPos = %d\n", sPos, ePos, ePos - sPos);
       DBParse((unsigned char*)sIn, sPos+kl+cl, ePos - sPos - kl - cl * 2);//两个二进制分割符中的数据
       sPos = substring(ePos, sIn, sl, key);
    }
    return 1;
    }

    /*
    函数介绍:上传文件出口函数
    输入参数:name = 表单名
    输出参数:
    返回值:返回PUploadQueue结构
    */
    PUploadQueue UExport(PUploadQueue *Q, char *name)
    {
    PUploadQueue PQ = *Q;
    PUploadQueue R = NULL;
    while(PQ != NULL)
    {
       if (!strcmp((char*)PQ->name, name))
       {
        R = PQ;
        break;
       }
       PQ= PQ->next;//指向队列中下个元素
    }
    return R;
    }

    void InitCUpload(PCUpload p)
    {
    /*
    p->GetBoundary = &GetBoundary;
    p->GetFormType = &GetFormType;
    p->GetFormName = &GetFormName;
    p->GetFileExtendA = &GetFileExtendA;
    p->GetFileExtendB = &GetFileExtendB;
    p->DBParse = &DBParse;
    p->UImport = &UImport;
    p->UExport = &UExport;
    */
    }

    /**************queue*****************/
    //以下就是队列了,就不用解释了,都是基本知识
    /*
    函数介绍:初始化队列函数
    输入参数:队列结构的对象p
    输出参数:
    返回值:
    */
    void InitQueue(PQueue p){
    p->IsInit = 1;
    p->serial = 0;   //队列元素个数
    //p->push = &push;
    //p->pop = &pop;
    //p->IsEmpty = &IsEmpty;
    //p->SaveQueue = &SaveQueue;
    }
    /*
    函数介绍:获得环境变量函数
    输入参数:环境变量参数
    输出参数:环境变量值
    返回值:
    */
    void GetEnv(string& szValue, const char* lpszName)
    {
    char *lpszValue= getenv(lpszName);
    szValue = lpszValue != 0 ? lpszValue : "";
    }

    public:
    CCGIParamsEx()
       :UQ(0)
       ,HTTPBuffer(0)
       ,REQUEST_METHOD(0)
       ,CONTENT_TYPE(0)
       ,CONTENT_LENGTH(0)
       ,REMOTE_ADDR(0)
       ,ulVersion((unsigned long)-1)

    {
    }
    int Init()
    {
       GetCookies(m_cookies); //以';'为分割符解码
       GetCGIParams(m_urlParams);//以'&'为分割符解码

       GetEnv(m_szRemoteAddr, "REMOTE_ADDR");

       unsigned long i = 0;
       long k = 0;
       char *lpszCookies = 0;
       char boundary[255]={0}, szLog[256]={0};
       time_t timer = time(0);
       struct tm * now = localtime(&timer);

       InitHTTPRequest(&R);

    //   g_Log.Format("[debug] %d", __LINE__);
    //   return 0;

    //   g_Log.Format("[info] %d", __LINE__);

       if (REQUEST_METHOD==NULL)
       {
    //   g_Log.Format("[info] %d", __LINE__);
    //    printf("This is CGI program only!\n");
       }
       else if (!strcmp((char*)REQUEST_METHOD, HTTP_GET) || !strcmp((char*)REQUEST_METHOD, HTTP_POST))//用GET方法或用POST方法
       {
    //    g_Log.Format("[info] %d", __LINE__);

    //    printf("Status: 200%s", CGI_CRLF);
    //    printf("Content-Type: text/plain%s", CGI_CRLF);
       // printf("%s", CGI_CRLF);

        if (!GetBoundary(boundary))//获取二进制分割符
        {
         //printf("boundary: %s\n", boundary);
         UImport((char*)HTTPBuffer, atoi((char*)CONTENT_LENGTH), boundary);
        }

    //   g_Log.Format("[info] %d", __LINE__);

        Q.SaveQueue(m_FormDatas, UQ);//对全部Form域进行处理

    //    g_Log.Format("[info] %d", m_FormDatas.size());


        for (i=0;i<Q.serial;i++)
        {
    //     g_Log.Format("[info] %d", __LINE__);

         Q.pop(&UQ);//清空队列
        }
       }
       else
       {
    //    printf("Status: 200%s", CGI_CRLF);
    //    printf("Content-Type: text/html%s", CGI_CRLF);
    //    //printf("%s", CGI_CRLF);
    //    printf("Only GET or POST request can be response!\n");
       }
       if (R.IsInit==1)
       {
        if (HTTPBuffer!=NULL)
        {
         free(HTTPBuffer);//释放资源
        }
        //R.FreeHTTPEnv();
       }
       return 0;
    }

    public:
    string        m_szRemoteAddr;
    CGIFROMDATAS   m_FormDatas;
    CGIPARAMS    m_cookies;
    CGIPARAMS    m_urlParams;
    };


    #endif

  • 相关阅读:
    彻底卸载Win11小组件
    pydantic学习与使用9.枚举类型(enum) 上海
    FastAPI学习1.环境准备与基础入门 上海
    python asyncio 异步 I/O 协程(Coroutine)与运行 上海
    pydantic学习与使用10.日期时间类型(datetime) 上海
    pydantic学习与使用12.使用 Field 定制字段 上海
    pydantic学习与使用11.pycharm插件pydantic 语法提示功能 上海
    pydantic学习与使用8.requiredfields必填字段省略号( ...) 上海
    python笔记72 使用pathlib替代os.path 上海
    python asyncio 异步 I/O 实现并发http请求(asyncio + aiohttp) 上海
  • 原文地址:https://www.cnblogs.com/zzxap/p/2175774.html
Copyright © 2020-2023  润新知