该版本在module_request.c文件中增加了url_decode的静态C函数,该函数用于对字符串进行url解码,同时新增builtin模块,该模块对应的C文件为:module_builtin.c,该模块目前有两个模块函数:bltArray和bltIterArray,bltArray用于创建数组,bltIterArray用于迭代数组成员...

    页面导航: 项目下载地址:

    zenglServer v0.1.1 源代码的相关地址:https://github.com/zenglong/zenglServer

zenglServer v0.1.1:

    该版本在module_request.c文件中增加了url_decode的静态C函数,该函数用于对字符串进行url解码,例如:%E7%A8%8B%E5%BA%8F%E5%91%98 解码后对应的就是UTF8编码的字符串“程序员”,相关C代码如下:

/**
 * 对str字符串参数进行url解码,
 * 例如:%E7%A8%8B%E5%BA%8F%E5%91%98 解码后对应的就是UTF8编码的字符串“程序员”
 * 通过将%E7转为0xE7的字节,%A8转为0xA8的字节,从而实现解码
 */
static char * url_decode(char * str)
{
    int str_len = strlen(str);
    char e_char[] = "00";
    for(int i = 0; i < str_len;i++)
    {
        switch(str[i])
        {
        case '%':
            if(str[i+1] == '\0')
                return str;
            if(isxdigit(str[i+1]) && isxdigit(str[i+2]))
            {
                e_char[0] = str[i+1];
                e_char[1] = str[i+2];
                long int x = strtol(e_char, NULL, 16);
                /* remove the hex */
                memmove(&str[i+1], &str[i+3], strlen(&str[i+3])+1);
                str_len -= 2;
                str[i] = x;
            }
            break;
        case '+':
            str[i] = ' ';
            break;
        }
    }
    return str;
}


    rqtGetQuery模块函数对应的C函数module_request_GetQuery就利用该静态函数对查询字符串进行url解码:

/**
 * rqtGetQuery模块函数,用于返回查询字符串的哈希数组形式
 * 例如:
 * querys = rqtGetQuery();
 * print 'querys[\'name\']: ' + querys['name'] + '<br/>';
 * print 'querys[\'job\']: ' + querys['job'] + '<br/>';
 * 对于 GET /test.zl?name=zengl&job=programmer HTTP/1.1 的http请求,
 * 上面例子显示的结果就是:
 * querys['name']: zengl
 * querys['job']: programmer
 *
 * 该模块函数会自动将key:value进行url解码,例如:
 * 对于 GET /v0_1_1/test.zl?%E5%B7%A5%E4%BD%9C=%E7%BC%96%E7%A8%8B HTTP/1.1 的http请求,
 * 解析后的数组成员为:
 * 工作: 编程
 * 其中%E5%B7%A5%E4%BD%9C解码为UTF8字符串“工作”,%E7%BC%96%E7%A8%8B则解码为UTF8字符串“编程”
 *
 * 该模块函数只会在第一次调用时,创建哈希数组,之后再调用该模块函数时,就会直接将之前创建过的数组返回
 */
ZL_EXP_VOID module_request_GetQuery(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
    ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}};
    MAIN_DATA * my_data = zenglApi_GetExtraData(VM_ARG, "my_data");
    MY_PARSER_DATA * my_parser_data = my_data->my_parser_data;
    struct http_parser_url * url_parser = &my_parser_data->url_parser;

    if(my_data->query_memblock.ptr == ZL_EXP_NULL) {
        if(zenglApi_CreateMemBlock(VM_ARG,&my_data->query_memblock,0) == -1) {
            zenglApi_Exit(VM_ARG,zenglApi_GetErrorString(VM_ARG));
        }
        zenglApi_AddMemBlockRefCount(VM_ARG,&my_data->query_memblock,1); // 手动增加该内存块的引用计数值,使其不会在脚本函数返回时,被释放掉。
        if((url_parser->field_set & (1 << UF_QUERY)) && (url_parser->field_data[UF_QUERY].len > 0)) {
            ZL_EXP_CHAR * q = my_parser_data->request_url.str + url_parser->field_data[UF_QUERY].off;
            ZL_EXP_INT q_len = url_parser->field_data[UF_QUERY].len;
            ZL_EXP_INT k = -1;
            ZL_EXP_INT v = -1;
            ZL_EXP_CHAR * decode_k = ZL_EXP_NULL;
            ZL_EXP_CHAR * decode_v = ZL_EXP_NULL;
            for(ZL_EXP_INT i = 0; i <= q_len; i++) {
                if(k == -1 && q[i] != '=' && q[i] != '&') {
                    k = i;
                }
                switch(q[i]) {
                case '=':
                    v = i + 1;
                    break;
                case '&':
                case '#':
                case STR_NULL:
                    if(k >= 0 && v > 0) {
                        ZL_EXP_CHAR prev_v_char = q[v - 1];
                        ZL_EXP_CHAR current_char = q[i];
                        q[i] = q[v - 1] = STR_NULL;
                        if(decode_k == ZL_EXP_NULL)
                            decode_k = zenglApi_AllocMem(VM_ARG, (strlen(&q[k]) + 1));
                        else
                            decode_k = zenglApi_ReAllocMem(VM_ARG, decode_k, (strlen(&q[k]) + 1));
                        strcpy(decode_k, &q[k]);
                        if(decode_v == ZL_EXP_NULL)
                            decode_v = zenglApi_AllocMem(VM_ARG, (strlen(&q[v]) + 1));
                        else
                            decode_v = zenglApi_ReAllocMem(VM_ARG, decode_v, (strlen(&q[v]) + 1));
                        strcpy(decode_v, &q[v]);
                        arg.type = ZL_EXP_FAT_STR;
                        arg.val.str = url_decode(decode_v);
                        zenglApi_SetMemBlockByHashKey(VM_ARG, &my_data->query_memblock, url_decode(decode_k), &arg);
                        q[v - 1] = prev_v_char;
                        if(current_char != STR_NULL)
                            q[i] = current_char;
                        k = v = -1;
                    }
                    else {
                        k = v = -1;
                    }
                    break;
                }
            }
            if(decode_k != ZL_EXP_NULL)
                zenglApi_FreeMem(VM_ARG, decode_k);
            if(decode_v != ZL_EXP_NULL)
                zenglApi_FreeMem(VM_ARG, decode_v);
        }
        zenglApi_SetRetValAsMemBlock(VM_ARG,&my_data->query_memblock);
    }
    else {
        zenglApi_SetRetValAsMemBlock(VM_ARG,&my_data->query_memblock);
    }
}


    在url解码时,上面的代码中会先通过zenglApi_AllocMem之类的zengl接口,分配堆空间,并将需要解码的字符串拷贝到堆空间中。接着再对该拷贝进行url_decode解码操作,这样就不会影响到原始的查询字符串数据。

    当前版本还新增了builtin模块,该模块对应的C文件为 module_builtin.c,该模块目前有两个模块函数:bltArray和bltIterArray,bltArray用于创建数组,bltIterArray用于迭代数组成员:

/*
 * module_builtin.c
 *
 *  Created on: 2017-7-16
 *      Author: zengl
 */

#include "module_builtin.h"

/**
 * bltIterArray模块函数,用于对数组成员进行迭代操作
 * 例如:
 * test['name'] = 'zengl';
 * test['job'] = 'programmer';
 * for(i=0;bltIterArray(test,&i,&k,&v);)
 *         print k +": " + v + '<br/>';
 * endfor
 * 该脚本在浏览器中的输出结果就是(<br/>会被浏览器做换行处理):
 * name: zengl
 * job: programmer
 *
 * 上面例子中,该模块函数的第一个参数test,是需要迭代的数组,
 * 第二个参数i是整数类型的变量的引用,用于表示需要访问的成员的索引值,
 * 该函数会将i索引位置处的名值对读取出来并分别设置到k,v参数中,所以k,v参数必须是引用,才能获取到值,
 * 如果i对应的成员是NONE类型(没有被赋予任何值)时,模块函数会跳过i,并自动往后去找有效的成员,
 * 第二个参数i之所以也是引用类型,是因为模块函数在结束时,会将下一次需要访问的索引值赋值给参数i
 *
 * 如果数组里的成员没有对应的key的话,第三个参数就会被设置为成员的整数索引值,例如:
 * test = bltArray('hello', 'world');
 * test[6] = "i'm end";
 * for(i=0;bltIterArray(test,&i,&k,&v);)
 *         print k +": " + v + '<br/>';
 * endfor
 * 结果就是:
 * 0: hello
 * 1: world
 * 6: i'm end
 * 上面例子中hello成员的索引值为0,world的索引值为1,"i'm end"成员的索引值为6,模块函数会自动跳过索引值为2,3,4,5的成员,
 * 因为这些成员并没有被赋予具体的值,其成员类型是NONE类型
 *
 * 可以只设置三个参数,如果只设置三个参数的话,就只会将数组中的值迭代出来,例如:
 * test['name'] = 'zengl';
 * test['job'] = 'programmer';
 * for(i=0;bltIterArray(test,&i,&v);)
 *         print v + '<br/>';
 * endfor
 * 结果会是:
 * zengl
 * programmer
 *
 * 当有成员可以进行迭代时,模块函数会返回整数1,否则返回整数0,因此,
 * 上面的for循环就可以根据,该模块函数的返回值来判断是否跳出循环,如果返回0,就跳出循环
 */
ZL_EXP_VOID module_builtin_iterate_array(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
    ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}};
    ZL_EXP_BOOL no_index = ZL_EXP_FALSE;
    if(argcount == 3)
        no_index = ZL_EXP_TRUE;
    else if(argcount != 4)
        zenglApi_Exit(VM_ARG,"usage: bltIterArray(array, &index, &[key|curindex], &value) | bltIterArray(array, &index, &value)");
    zenglApi_GetFunArg(VM_ARG,1,&arg);
    // 如果第一个参数不是数组之类的内存块,则无需迭代,直接返回0
    if(arg.type != ZL_EXP_FAT_MEMBLOCK) {
        zenglApi_SetRetVal(VM_ARG, ZL_EXP_FAT_INT, ZL_EXP_NULL, 0, 0);
        return;
    }
    ZENGL_EXPORT_MEMBLOCK memblock = {0};
    memblock = arg.val.memblock;

    zenglApi_GetFunArgInfo(VM_ARG,2,&arg);
    switch(arg.type){
    case ZL_EXP_FAT_ADDR:
    case ZL_EXP_FAT_ADDR_LOC:
    case ZL_EXP_FAT_ADDR_MEMBLK:
        break;
    default:
        zenglApi_Exit(VM_ARG,"second argument of bltIterArray must be address type");
        break;
    }

    zenglApi_GetFunArg(VM_ARG,2,&arg);
    if(arg.type != ZL_EXP_FAT_INT)
        zenglApi_Exit(VM_ARG,"second argument value of bltIterArray must be integer");
    ZL_EXP_INT index = (ZL_EXP_INT)arg.val.integer;
    ZENGL_EXPORT_MOD_FUN_ARG mblk_val = {ZL_EXP_FAT_NONE,{0}};
    ZL_EXP_INT size;
    zenglApi_GetMemBlockInfo(VM_ARG,&memblock,&size, ZL_EXP_NULL);
check_index:
    if(index < 0 || index >= size) {
        zenglApi_SetRetVal(VM_ARG, ZL_EXP_FAT_INT, ZL_EXP_NULL, 0, 0);
        return;
    }
    mblk_val = zenglApi_GetMemBlock(VM_ARG,&memblock,index + 1);
    if(mblk_val.type == ZL_EXP_FAT_NONE) {
        index++;
        goto check_index;
    }

    zenglApi_GetFunArgInfo(VM_ARG,3,&arg);
    switch(arg.type){
    case ZL_EXP_FAT_ADDR:
    case ZL_EXP_FAT_ADDR_LOC:
    case ZL_EXP_FAT_ADDR_MEMBLK:
        break;
    default:
        zenglApi_Exit(VM_ARG,"the third argument of bltIterArray must be address type");
        break;
    }

    ZL_EXP_CHAR * key;
    if(no_index == ZL_EXP_FALSE) {
        zenglApi_GetMemBlockHashKey(VM_ARG,&memblock,index,&key);
        if(key != ZL_EXP_NULL) {
            arg.type = ZL_EXP_FAT_STR;
            arg.val.str = key;
            zenglApi_SetFunArg(VM_ARG,3,&arg);
        }
        else {
            arg.type = ZL_EXP_FAT_INT;
            arg.val.integer = index;
            zenglApi_SetFunArg(VM_ARG,3,&arg);
        }

        zenglApi_GetFunArgInfo(VM_ARG,4,&arg);
        switch(arg.type){
        case ZL_EXP_FAT_ADDR:
        case ZL_EXP_FAT_ADDR_LOC:
        case ZL_EXP_FAT_ADDR_MEMBLK:
            break;
        default:
            zenglApi_Exit(VM_ARG,"the forth argument of bltIterArray must be address type");
            break;
        }

        zenglApi_SetFunArg(VM_ARG,4,&mblk_val);
    }
    else {
        zenglApi_SetFunArg(VM_ARG,3,&mblk_val);
    }
    arg.type = ZL_EXP_FAT_INT;
    arg.val.integer = index + 1;
    zenglApi_SetFunArg(VM_ARG,2,&arg);
    zenglApi_SetRetVal(VM_ARG, ZL_EXP_FAT_INT, ZL_EXP_NULL, 1, 0);
}

/**
 * builtin模块的初始化函数,里面设置了与该模块相关的各个模块函数及其相关的处理句柄
 */
ZL_EXP_VOID module_builtin_init(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT moduleID)
{
    zenglApi_SetModFunHandle(VM_ARG,moduleID,"bltArray",zenglApiBMF_array);
    zenglApi_SetModFunHandle(VM_ARG,moduleID,"bltIterArray",module_builtin_iterate_array);
}


    在新增的my_webroot/v0_1_1/test.zl测试脚本中,可以看到这两个模块函数的用法:

use builtin;
use request;

print '<!Doctype html>';
// 设置utf-8编码,中间的-不能少,ie10不识别utf8,只识别utf-8
print '<html><head><meta http-equiv="content-type" content="text/html;charset=utf-8" /></head><body>';

headers = rqtGetHeaders();
print 'user agent: ' + headers['User-Agent'] + '<br/>';

query_string = rqtGetQueryAsString();
if(query_string)
    print 'query string: ' + query_string + '<br/>';
    querys = rqtGetQuery();
    // 通过bltIterArray模块函数来迭代数组成员
    for(i=0;bltIterArray(querys,&i,&k,&v);)
        print k +": " + v + '<br/>';
    endfor
endif

print '<br/>';
test = bltArray('hello', 'world');
test[6] = "i'm end";
for(i=0;bltIterArray(test,&i,&k,&v);)
    print k +": " + v + '<br/>';
endfor

print '</body></html>';


    对于 GET /v0_1_1/test.zl?name=zengl&job=%E7%A8%8B%E5%BA%8F%E5%91%98 HTTP/1.1 的请求,作者的FireFox浏览器的反馈结果如下:

user agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:54.0) Gecko/20100101 Firefox/54.0
query string: name=zengl&job=%E7%A8%8B%E5%BA%8F%E5%91%98
name: zengl
job: 程序员

0: hello
1: world
6: i'm end


    当前版本还新增了common_header.h头文件:

/*
 * common_header.h
 *
 *  Created on: 2017-7-16
 *      Author: zengl
 */

#ifndef COMMON_HEADER_H_
#define COMMON_HEADER_H_

#ifndef ZL_EXP_OS_IN_LINUX
    #define ZL_EXP_OS_IN_LINUX
#endif

#include "zengl/linux/zengl_exportfuns.h"

#endif /* COMMON_HEADER_H_ */


    其他的main.h,module_request.h,module_builtin.h等需要引入zengl_exportfuns.h的,直接包含common_header.h头文件即可:

/*
 * module_builtin.h
 *
 *  Created on: 2017-7-16
 *      Author: zengl
 */

#ifndef MODULE_BUILTIN_H_
#define MODULE_BUILTIN_H_

#include "common_header.h"

/**
 * builtin模块的初始化函数,里面设置了与该模块相关的各个模块函数及其相关的处理句柄
 */
ZL_EXP_VOID module_builtin_init(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT moduleID);

#endif /* MODULE_BUILTIN_H_ */


结束语:

    再渺小的人也能改变世界

——  《魔戒》
 
上下篇

下一篇: zengl v1.7.2, zenglServer v0.2.0

上一篇: zengl v1.6.0-v1.7.1, zenglServer v0.1.0

相关文章

zengl编程语言v0.0.23,SDL游戏开发,类定义

zengl v1.6.0-v1.7.1, zenglServer v0.1.0

zengl v1.7.2, zenglServer v0.2.0

zengl编程语言v0.0.22实现switch...case

zengl编程语言v0.0.21汇编级调试

zengl v1.2.1 修复函数调用BUG