zengl v1.7.2版本增加了zenglApi_GetMemBlockByHashKey接口函数,通过此接口,可以在模块函数内根据字符串key来获取数组中的成员。在zenglApi.c文件中可以看到此接口函数的定义,zenglServer v0.2.0 支持表单以Post方式上传数据(包括文件上传)...

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

    zengl language v1.7.2 源代码的相关地址:https://github.com/zenglong/zengl_language/

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

zengl v1.7.2:

    该版本增加了zenglApi_GetMemBlockByHashKey接口函数,通过此接口,可以在模块函数内根据字符串key来获取数组中的成员。在zenglApi.c文件中可以看到此接口函数的定义:

/*根据key来获取数组等内存块中的成员,此接口会先将key转为索引值,再根据索引值和上面的zenglApi_GetMemBlock接口来返回对应的成员*/
ZL_EXPORT ZENGL_EXPORT_MOD_FUN_ARG zenglApi_GetMemBlockByHashKey(ZL_EXP_VOID * VM_ARG,ZENGL_EXPORT_MEMBLOCK * memblock,ZL_EXP_CHAR * key)
{
    ZENGL_VM_TYPE * VM = (ZENGL_VM_TYPE *)VM_ARG;
    ZENGL_EXPORT_MOD_FUN_ARG retval = {0};
    ZL_INT index;
    ZL_CHAR * ApiName = "zenglApi_GetMemBlockByHashKey";
    if(VM->signer != ZL_VM_SIGNER) //通过虚拟机签名判断是否是有效的虚拟机
    {
        retval.type = ZL_EXP_FAT_INVALID;
        return retval;
    }
    switch(VM->ApiState)
    {
    case ZL_API_ST_MOD_FUN_HANDLE:
        break;
    default:
        VM->run.SetApiErrorEx(VM_ARG,ZL_ERR_VM_API_INVALID_CALL_POSITION, ApiName , ApiName);
        retval.type = ZL_EXP_FAT_INVALID;
        return retval;
        break;
    }
    index = zenglrun_getIndexFromHashCodeTable(VM_ARG, (ZENGL_RUN_VIRTUAL_MEM_LIST *)memblock->ptr, key);
    return zenglApi_GetMemBlock(VM_ARG, memblock, index + 1);
}


    在main.c文件内,新增的bltTestPrintByArrayKey模块函数,就使用了该接口:

/*bltTestPrintByArrayKey模块函数(仅供测试),用于测试zenglApi_GetMemBlockByHashKey接口函数*/
ZL_EXP_VOID main_builtin_test_print_by_array_key(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
    ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}};
    ZENGL_EXPORT_MEMBLOCK memblock = {0};
    ZENGL_EXPORT_MOD_FUN_ARG mblk_val = {ZL_EXP_FAT_NONE,{0}};
    if(argcount != 2)
        zenglApi_Exit(VM_ARG,"usage:bltTestPrintByArrayKey(array, key)");
    zenglApi_GetFunArg(VM_ARG,1,&arg);
    if(arg.type != ZL_EXP_FAT_MEMBLOCK)
        zenglApi_Exit(VM_ARG,"bltTestPrintByArrayKey函数的第一个参数必须是哈希数组");
    memblock = arg.val.memblock;
    zenglApi_GetFunArg(VM_ARG,2,&arg);
    if(arg.type != ZL_EXP_FAT_STR)
        zenglApi_Exit(VM_ARG,"bltTestPrintByArrayKey函数的第二个参数必须是字符串,表示数组中的key");
    // 通过zenglApi_GetMemBlockByHashKey接口,实现在模块函数中,根据字符串key来获取数组的成员
    mblk_val = zenglApi_GetMemBlockByHashKey(VM_ARG, &memblock, arg.val.str);
    switch(mblk_val.type)
    {
    case ZL_EXP_FAT_INT:
        printf("%ld\n", mblk_val.val.integer);
        break;
    case ZL_EXP_FAT_FLOAT:
        printf("%.16g\n", mblk_val.val.floatnum);
        break;
    case ZL_EXP_FAT_STR:
        printf("%s\n", mblk_val.val.str);
        break;
    case ZL_EXP_FAT_MEMBLOCK:
        main_print_array(VM_ARG, mblk_val.val.memblock, 0);
        break;
    default:
        zenglApi_Exit(VM_ARG,"bltTestPrintByArrayKey函数检测到无效的类型");
        break;
    }
}


    新增测试脚本 test_scripts/v1.7.2/test.zl 用于测试上面的模块函数:

use builtin;

a['name'] = 'zengl';
a['job'] = 'programmer';
a['hobby'] = array('play game', 'watch movie', 'write code');

print '\nmy name:';
bltTestPrintByArrayKey(a, 'name');

print '\nmy job:';
bltTestPrintByArrayKey(a, 'job');

print '\nmy hobby:';
bltTestPrintByArrayKey(a, 'hobby');
print '';


    执行结果如下:

zengl@zengl-ubuntu:~/zengl_v1.7.2/zengl_language/linux$ ./zengl test_scripts/v1.7.2/test.zl 
run(编译执行中)...

my name:
zengl

my job:
programmer

my hobby:
[0] play game
[1] watch movie
[2] write code

run finished(编译执行结束)
zengl@zengl-ubuntu:~/zengl_v1.7.2/zengl_language/linux$ 


    zengl v1.7.2的代码只在windows,linux和mac OSX中进行过基本的测试。

zenglServer v0.2.0:

    该版本支持表单以Post方式上传数据(包括文件上传),在my_webroot中增加v0_2_0目录,该目录中的form.html是用于测试表单Post上传的html页面,该目录中的post.zl则是表单通过post方式上传后的zengl脚本处理程式。form.html的代码如下:

<!doctype html>
<html>
<head>
    <meta http-equiv="content-type" content="text/html;charset=utf-8" />
    <title>hello world</title>
</head>
<body>
    <form action="post.zl" method="post" enctype="multipart/form-data">
        <p><input name='ti"tl"e' value="" type="text"></p>
        <p><input name="description" value="" type="text"></p>
        <p><textarea name="content"></textarea></p>
        <p><input name="我的文件" type="file"></p>
        <input name="sb" value="Submit" type="submit">
    </form>
</body>
</html>


    post.zl的代码如下:

use builtin;
use request;

print '<!Doctype html>';
print '<html><head><meta http-equiv="content-type" content="text/html;charset=utf-8" /></head><body>';

headers = rqtGetHeaders();
print 'Content-Length:' + headers['Content-Length'] + "<br/>";

body = rqtGetBody(&body_count, &body_source);
if(headers['Content-Length'] > body_count)
    print 'body count: ' + body_count + '<br/>';
    print '<h3>your content is too big, maybe the upload file is too big!</h3>';
elif(body_count > 0)
    print 'request body[count/' + body_count + ']: ' + body + '<br/><br/>';
    body_array = rqtGetBodyAsArray();
    for(i=0;bltIterArray(body_array,&i,&k,&v);)
        print k +": " + v + '<br/>';
        for(j=0;bltIterArray(v,&j,&inner_k,&inner_v);)
            print "&nbsp;&nbsp;" + inner_k + ": " + inner_v + "<br/>";
            if(inner_k == 'filename')
                bltWriteFile(v['filename'], v['content_ptr'], v['length']);
            endif
        endfor
    endfor
    bltWriteFile('body.log', body);
    bltWriteFile('body_source.log', body_source, body_count);
endif

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


    在这个脚本中,用到了当前版本中最主要的三个模块函数:rqtGetBody,rqtGetBodyAsArray 以及 bltWriteFile,前两个模块函数定义在request模块中(该模块对应的C代码位于module_request.c中):

/**
 * rqtGetBody模块函数,用于返回请求的body(主体数据),如果没有请求主体数据,则返回空字符串,
 * 例如:
 * body = rqtGetBody();
 * if(body)
 *         print 'request body: ' + body;
 * endif
 * 对于application/x-www-form-urlencoded类型(表单提交时Content-Type的默认类型)的post请求,该例子可能显示的结果为:
 * request body: title=hello&description=world&content=test&sb=Submit
 * 如果指定了第一个参数,那么模块函数会将body(主体数据)的总的字节数写入到该参数中,
 * 例如:rqtGetBody(&body_count) 会将字节数写入到body_count变量里,
 * 如果指定了第二个参数,那么模块函数还会将body(主体数据)的起始字节的指针值写入到该参数中,
 * 例如:rqtGetBody(&body_count, &body_ptr) 会将字节数写入到body_count变量,同时将指针值写入到body_ptr变量,
 * 获取到指针值后,就可以通过bltWriteFile模块函数将body的所有数据(包括上传文件的二进制数据)都写入到文件中,
 * 当然也可以通过其他模块函数,利用指针去做别的事情,
 * 第一个和第二个参数必须是address type(引用类型)
 */
ZL_EXP_VOID module_request_GetBody(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;

    if(argcount == 1 || argcount == 2) {
        zenglApi_GetFunArgInfo(VM_ARG,1,&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 first argument of rqtGetBody must be address type");
            break;
        }
        arg.type = ZL_EXP_FAT_INT;
        // 如果追加了NULL字符(正常情况都会追加),那么body的count的值,会比实际追加的请求主体数据的字节数多一个字节,这里我们只返回实际的字节数
        if(my_parser_data->is_request_body_append_null == ZL_EXP_TRUE)
            arg.val.integer = (my_parser_data->request_body.count - 1);
        else
            arg.val.integer = my_parser_data->request_body.count;
        zenglApi_SetFunArg(VM_ARG,1,&arg);
        if(argcount == 2) {
            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,"the second argument of rqtGetBody must be address type");
                break;
            }
            arg.type = ZL_EXP_FAT_INT;
            arg.val.integer = (ZL_EXP_LONG)my_parser_data->request_body.str;
            zenglApi_SetFunArg(VM_ARG,2,&arg);
        }
    }
    else if(argcount != 0) {
        zenglApi_Exit(VM_ARG,"usage: rqtGetBody() | rqtGetBody(&body_count) | rqtGetBody(&body_count, &body_ptr)");
    }

    if(my_parser_data->request_body.str != PTR_NULL && my_parser_data->request_body.count > 0) {
        zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_STR, my_parser_data->request_body.str, 0, 0);
    }
    else {
        zenglApi_SetRetVal(VM_ARG,ZL_EXP_FAT_STR, "", 0, 0);
    }
}

...........................................................

/**
 * rqtGetBodyAsArray模块函数,主要用于将POST请求的主体数据转为数组的形式返回,
 * 该模块函数既可以解析Content-Type为application/x-www-form-urlencoded的表单请求,
 * 也可以解析Content-Type为multipart/form-data的请求
 * 例如:
    body_array = rqtGetBodyAsArray();
    for(i=0;bltIterArray(body_array,&i,&k,&v);)
        print k +": " + v + '<br/>';
        for(j=0;bltIterArray(v,&j,&inner_k,&inner_v);)
            print "&nbsp;&nbsp;" + inner_k + ": " + inner_v + "<br/>";
            if(inner_k == 'filename')
                bltWriteFile(v['filename'], v['content_ptr'], v['length']);
            endif
        endfor
    endfor

    * 对于下面这个application/x-www-form-urlencoded类型的请求:

    POST /v0_2_0/post.zl HTTP/1.1
    Host: 192.168.0.103:8083
    User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:55.0) Gecko/20100101 Firefox/55.0
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,.....
    Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
    Accept-Encoding: gzip, deflate
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 116
    Referer: http://192.168.0.103:8083/v0_2_0/form.html
    Connection: keep-alive
    Upgrade-Insecure-Requests: 1

    ti%22tl%22e=%E6%A0%87%E9%A2%98&description=%E6%8F%8F%E8%BF%B0&content=%E5%86%85%E5%AE%B9%E9%83%A8%E5%88%86&sb=Submit

    * 脚本在执行时,得到的结果如下:

    ti"tl"e: 标题
    description: 描述
    content: 内容部分
    sb: Submit

    脚本会对主体数据中的名值对进行url解码,例如,ti%22tl%22e被解码为ti"tl"e,%E6%A0%87%E9%A2%98被解码为标题等等

    * 对于下面这个multipart/form-data类型的请求:

    POST /v0_2_0/post.zl HTTP/1.1
    ................................
    Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryJLB14p6QUQR9oO4G
    ................................

    ------WebKitFormBoundaryJLB14p6QUQR9oO4G
    Content-Disposition: form-data; name="ti%22tl%22e"

    测试。。!
    ------WebKitFormBoundaryJLB14p6QUQR9oO4G
    Content-Disposition: form-data; name="description"

    描述。。
    ------WebKitFormBoundaryJLB14p6QUQR9oO4G
    Content-Disposition: form-data; name="content"

    内容哈哈。。。
    ------WebKitFormBoundaryJLB14p6QUQR9oO4G
    Content-Disposition: form-data; name="我的文件"; filename="timg.jpg"
    Content-Type: image/jpeg

    .................................

    * 脚本在执行时,得到的结果如下:

    ti"tl"e: 测试。。!
    description: 描述。。
    content: 内容哈哈。。。
    我的文件:
      filename: timg.jpg
      type: image/jpeg
      content_ptr: 140540188302856
      length: 16212
    sb: Submit

    请求中的name被转为了数组的字符串key,具体的内容则被转为了该key对应的值,
    如果某个part上传的是文件,那么key对应的值将会是一个数组,该数组中包含了
    filename文件名,type文件类型,content_ptr指向文件数据的指针,以及length文件长度
    脚本中就可以通过 bltWriteFile(v['filename'], v['content_ptr'], v['length']);
    将POST上传的文件数据保存到某个文件中
 */
ZL_EXP_VOID module_request_GetBodyAsArray(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
    ZL_EXP_CHAR * content_type = ZL_EXP_NULL;
    MAIN_DATA * my_data = zenglApi_GetExtraData(VM_ARG, "my_data");
    if(my_data->body_memblock.ptr == ZL_EXP_NULL) {
        if(zenglApi_CreateMemBlock(VM_ARG,&my_data->body_memblock,0) == -1) {
            zenglApi_Exit(VM_ARG,zenglApi_GetErrorString(VM_ARG));
        }
        zenglApi_AddMemBlockRefCount(VM_ARG,&my_data->body_memblock,1); // 手动增加该内存块的引用计数值,使其不会在脚本函数返回时,被释放掉。
        MY_PARSER_DATA * my_parser_data = my_data->my_parser_data;
        if(my_parser_data->request_body.str != PTR_NULL && my_parser_data->request_body.count > 0) {
            get_headers(VM_ARG, my_data);
            ZENGL_EXPORT_MOD_FUN_ARG retval = zenglApi_GetMemBlockByHashKey(VM_ARG,&my_data->headers_memblock, "Content-Type");
            if(retval.type == ZL_EXP_FAT_STR) {
                content_type = retval.val.str;
                if(strcmp(content_type, "application/x-www-form-urlencoded") == 0) {
                    ZL_EXP_CHAR * q = my_parser_data->request_body.str;
                    ZL_EXP_INT q_len = my_parser_data->request_body.count;
                    parse_urlencoded_str_to_memblock(VM_ARG, q, q_len, &my_data->body_memblock);
                }
                else if(strstr(content_type, "multipart/form-data")) {
                    const ZL_EXP_CHAR * boundary_key = "boundary=";
                    ZL_EXP_CHAR * boundary = strstr(content_type, boundary_key);
                    if(boundary) {
                        ZL_EXP_INT content_type_length = strlen(content_type);
                        boundary += strlen(boundary_key);
                        if(boundary < (content_type + content_type_length)) {
                            ZL_EXP_CHAR * q = my_parser_data->request_body.str;
                            ZL_EXP_INT q_len = my_parser_data->request_body.count;
                            printf("%s[debug]\n", boundary); // debug
                            multipart_parser_settings callbacks;
                            memset(&callbacks, 0, sizeof(multipart_parser_settings));
                            callbacks.on_header_field = read_multipart_header_name;
                            callbacks.on_header_value = read_multipart_header_value;
                            callbacks.on_headers_complete = on_multipart_headers_complete;
                            callbacks.on_part_data = read_multipart_data;
                            callbacks.on_part_data_end = on_multipart_data_end;
                            multipart_parser* parser = multipart_parser_init(boundary, &callbacks);
                            my_multipart_data my_mp_data = {0};
                            my_mp_data.VM_ARG = VM_ARG;
                            my_mp_data.memblock = &my_data->body_memblock;
                            multipart_parser_set_data(parser, &my_mp_data);
                            multipart_parser_execute(parser, q, q_len);
                            multipart_free_all_zlmem(parser);
                            multipart_parser_free(parser);
                        }
                    }
                }
            }
        }
        zenglApi_SetRetValAsMemBlock(VM_ARG,&my_data->body_memblock);
    }
    else {
        zenglApi_SetRetValAsMemBlock(VM_ARG,&my_data->body_memblock);
    }
}


    rqtGetBodyAsArray模块函数在解析multipart/form-data类型的表单请求时,是使用multipart_parser这个第三方的库来解析请求的主体数据的,不过,每个part的请求头中的值,还需要自己写状态机来做进一步的解析,可以参考module_request.c文件中的parse_multipart_header_value函数,该函数的具体代码不在此列出,需要读者根据相关注释来进行分析。

    bltWriteFile模块函数定义在builtin模块里(该模块对应的C代码位于module_builtin.c文件中):

/**
 * bltWriteFile模块函数,用于将字符串或者指针所指向的数据写入到指定的文件中
 * 例如:
 * body = rqtGetBody(&body_count, &body_source);
 * bltWriteFile('body.log', body);
 * bltWriteFile('body_source.log', body_source, body_count);
 * 该例子中,rqtGetBody会返回请求主体数据的字符串格式,同时将主体数据的字节数及指针值分别写入
 * 到body_count和body_source变量里,当然指针在zengl内部是以和指针长度一致的长整数的形式保存的,
 * 当请求主体数据中只包含字符串时,上面两个bltWriteFile写入文件的数据会是一样的,
 * 当主体数据中还包含了上传的文件时,两者就不一样了,body只会显示字符串能显示的开头的部分,直到被NULL字符阻止,
 * body_source配合body_count则可以将所有主体数据(包括上传的文件的二进制数据)都写入到文件中,
 * 从例子中可以看出,bltWriteFile模块函数既可以写入字符串,也可以写入指针指向的二进制数据,通过
 * 第三个参数可以限制数据写入的长度
 */
ZL_EXP_VOID module_builtin_write_file(ZL_EXP_VOID * VM_ARG,ZL_EXP_INT argcount)
{
    ZENGL_EXPORT_MOD_FUN_ARG arg = {ZL_EXP_FAT_NONE,{0}};
    if(argcount != 3 && argcount != 2)
        zenglApi_Exit(VM_ARG,"usage: bltWriteFile(filename, [ptr|string], length) | bltWriteFile(filename, string)");
    zenglApi_GetFunArg(VM_ARG,1,&arg);
    if(arg.type != ZL_EXP_FAT_STR) {
        zenglApi_Exit(VM_ARG,"the first argument of bltWriteFile must be string");
    }
    char * filename = arg.val.str;
    zenglApi_GetFunArg(VM_ARG,2,&arg);
    void * ptr = ZL_EXP_NULL;
    char * string = ZL_EXP_NULL;
    if(arg.type == ZL_EXP_FAT_STR) {
        string = arg.val.str;
        ptr = string;
    }
    else if(arg.type == ZL_EXP_FAT_INT) {
        ptr = (void *)arg.val.integer;
    }
    else {
        zenglApi_Exit(VM_ARG,"the second argument of bltWriteFile must be integer or string");
    }
    int length = 0;
    if(argcount == 3) {
        zenglApi_GetFunArg(VM_ARG,3,&arg);
        if(arg.type != ZL_EXP_FAT_INT) {
            zenglApi_Exit(VM_ARG,"the third argument of bltWriteFile must be integer");
        }
        length = (int)arg.val.integer;
    }
    else if(string != ZL_EXP_NULL) {
        length = strlen(string);
    }
    else {
        zenglApi_Exit(VM_ARG,"the length needed by bltWriteFile can't be detected");
    }
    char full_path[FULL_PATH_SIZE];
    MAIN_DATA * my_data = zenglApi_GetExtraData(VM_ARG, "my_data");
    char * right_slash = strrchr(my_data->full_path, '/');
    if(right_slash) {
        int append_length = right_slash - my_data->full_path + 1;
        strncpy(full_path, my_data->full_path, append_length);
        append_length += main_full_path_append(full_path, append_length, FULL_PATH_SIZE, filename);
        full_path[append_length] = '\0';
    }
    else {
        char * webroot = main_get_webroot();
        int append_length = 0;
        append_length += main_full_path_append(full_path, append_length, FULL_PATH_SIZE, webroot);
        if(filename[0] != '/')
            append_length += main_full_path_append(full_path, append_length, FULL_PATH_SIZE, "/");
        append_length += main_full_path_append(full_path, append_length, FULL_PATH_SIZE, filename);
        full_path[append_length] = '\0';
    }
    FILE * fp = fopen(full_path, "wb");
    fwrite(ptr, 1, length, fp);
    fclose(fp);
}

/**
 * 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);
    zenglApi_SetModFunHandle(VM_ARG,moduleID,"bltWriteFile",module_builtin_write_file);
}


    要测试表单上传功能,我们先编译并运行zenglServer:

zengl@zengl-ubuntu:~/zenglServer$ make clean
rm -fv zenglServer
rm -fv *.o
rm -fv zengl/linux/*.o
rm -fv zengl/linux/libzengl.a
zengl@zengl-ubuntu:~/zenglServer$ make
cd zengl/linux && make libzengl.a
make[1]: Entering directory '/home/zengl/zenglServer/zengl/linux'
gcc -D ZL_LANG_EN_WITH_CH -g3 -ggdb -O0 -std=c99 -fvisibility=hidden -fPIC -c zengl_main.c zengl_parser.c zengl_symbol.c zengl_locals.c zengl_assemble.c zengl_ld.c zenglrun_main.c zenglrun_func.c zenglrun_hash_array.c zenglApi.c zenglApi_BltModFuns.c zenglDebug.c
ar rc libzengl.a zengl_main.o zengl_parser.o zengl_symbol.o zengl_locals.o zengl_assemble.o zengl_ld.o zenglrun_main.o zenglrun_func.o zenglrun_hash_array.o zenglApi.o zenglApi_BltModFuns.o zenglDebug.o
make[1]: Leaving directory '/home/zengl/zenglServer/zengl/linux'
gcc -g3 -ggdb -O0 -std=c99 main.c http_parser.c module_request.c module_builtin.c dynamic_string.c multipart_parser.c  main.h http_parser.h common_header.h module_request.h module_builtin.h dynamic_string.h multipart_parser.h  zengl/linux/zengl_exportfuns.h  -o zenglServer zengl/linux/libzengl.a -lpthread
zengl@zengl-ubuntu:~/zenglServer$ ./zenglServer
use default config: config.zl
*** config is in debug mode ***
run config.zl complete, config:
port: 8083 process_num: 1 thread_num_per_process: 1
webroot: my_webroot
bind done
accept sem initialized.



    假设zenglServer所在IP为192.168.0.103,通过浏览器访问 http://192.168.0.103:8083/v0_2_0/form.html ,界面如下:


图1:form.html表单页面

    点击Submit提交按钮后,结果如下:
 

图2:处理表单Post请求

    上传的文件会被保存到v0_2_0目录中,并以上传时的filename的值作为文件名,例如:上例中为icon.png,就可以通过访问 http://192.168.0.103:8083/v0_2_0/icon.png 来查看到:
 


图3:Post上传的图片显示

    当然,上传数据的总大小不能超过main.h头文件中REQUEST_BODY_STR_MAX_SIZE宏定义的200000字节(大约为195KB),超出范围,post.zl会输出your content is too big, maybe the upload file is too big! 的提示信息,读者可以上传一个比较大的几百K的图片来做测试:
 


图4:上传图片太大时的输出

    在发起表单Post请求时,在zenglServer所在的终端,也会输出相关的调试信息:
 


图5:表单请求相关调试信息
 
    限于篇幅,当前版本相关的内容就介绍到这,相关C源代码请读者结合注释来进行分析。
 
结束语:



    I'm the king of the world! ("我是世界之王!")

——  《泰坦尼克号》
 
上下篇

下一篇: zengl v1.7.3 获取数组之类的内存块中非NONE成员的数量

上一篇: zenglServer v0.1.1 url解码,新增builtin模块实现数组成员的迭代操作

相关文章

zengl v1.8.1 修复bug

zengl v1.9.0 增加self特殊类名,增加zenglApi_SetDefLookupHandle等接口

zengl编程语言v0.0.13函数中实现global和return

zengl编程语言v0.0.5构建符号表汇编代码和虚拟机

zengl编程语言 v1.0.3 完善文件出错信息

zengl v1.8.2 修复内存泄漏