zengl v1.7.2版本增加了zenglApi_GetMemBlockByHashKey接口函数,通过此接口,可以在模块函数内根据字符串key来获取数组中的成员。在zenglApi.c文件中可以看到此接口函数的定义,zenglServer v0.2.0 支持表单以Post方式上传数据(包括文件上传)...
/*根据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); } |
/*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; } } |
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$ |
<!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> |
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 " " + 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模块函数,用于返回请求的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 " " + 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); } } |
/** * 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); } |
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. |