quickjs集成http功能
零、前言
默认的quickjs,是js引擎,需要自己移植类似curl库,才能使quickjs有http请求功能。js引擎+一些本地功能调用=js运行时。
一、libcurl库
这个库的安装或编译,也是比较麻烦的事情,特别是需要使其支持https访问,配置和编译更是麻烦。因此,还是使用上次提到的vcpkg。通过vcpkg search curl,然后根据需要安装对应支持ssl证书的。然后安装好的libcurl.dll libcrypto-3-x64.dll libssl-3-x64.dll 文件放到运行目录,同时也要复制对应include头文件到对应目录。
一个curl例子
1 #include <stdio.h> 2 #include "curl/curl.h" 3 4 5 size_t readData(void *ptr, size_t size, size_t nmemb, void *stream) 6 { 7 int len = size * nmemb; 8 char buf[10240] = {'\0'}; 9 strncpy(buf, ptr, len); 10 printf("=========================get Data=====================\n"); 11 printf("%s\n",buf); 12 printf("======================================================\n"); 13 return len; 14 } 15 16 int main() 17 { 18 CURL *curl; 19 CURLcode res; 20 curl = curl_easy_init(); 21 22 if (curl) 23 { 24 curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); 25 curl_easy_setopt(curl, CURLOPT_URL, "https://mock.apifox.com/m2/385371-0-default/33329355"); 26 27 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); 28 curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "https"); 29 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);//忽略证书检查 30 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); 31 32 struct curl_slist *headers = NULL; 33 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); 34 const char *data = "{}"; 35 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data); 36 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, readData); 37 38 res = curl_easy_perform(curl); 39 printf("res = %d\n", res); 40 if (res != CURLE_OK) 41 fprintf(stderr, "%s\n", curl_easy_strerror(res)); 42 43 } 44 curl_easy_cleanup(curl); 45 printf("Hello World!\n"); 46 return 0; 47 }
输出结果
1 D:\test\quickjs\examples\libuv>bin\main.exe 2 =========================get Data===================== 3 {"msg":"ok"} 4 ====================================================== 5 res = 0 6 Hello World!
CMakeList.txt
1 cmake_minimum_required(VERSION 3.10) 2 project(quickjs_libuv) 3 4 SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin) 5 6 include_directories(${PROJECT_SOURCE_DIR}) 7 include_directories(${PROJECT_SOURCE_DIR}/include) 8 9 link_directories(${PROJECT_SOURCE_DIR}/bin) 10 # add_executable(main main.c) 11 add_executable(main test_curl.c) 12 target_link_libraries(main quickjs uv curl)
二、quickjs绑定
1. 通过使用curl_easy_setopt函数设置 CURLOPT_WRITEDATA,实现请求结果回调。
1 size_t readData(void *ptr, size_t size, size_t nmemb, void *stream) 2 { 3 http_ctx_t *ctx = (http_ctx_t *)stream; 4 int len = size * nmemb; 5 char buf[10240] = {'\0'}; 6 strncpy(buf, ptr, len); 7 printf("=========================get Data=====================\n"); 8 // printf("%s\n", buf); 9 JSValue func = ctx->func; 10 JSValueConst this_obj = JS_UNDEFINED; 11 JSValueConst argv[1] = {JS_NewString(ctx->ctx, buf)}; 12 JSValue ret = JS_Call(ctx->ctx, func, this_obj, 1, argv); 13 JS_FreeValue(ctx->ctx, func); 14 if(JS_IsException(ret)){ 15 js_std_dump_error(ctx->ctx); 16 } 17 JS_FreeValue(ctx->ctx, argv[0]); 18 JS_FreeValue(ctx->ctx, ret); 19 printf("======================================================\n"); 20 return len; 21 }
2. 实现两种调用方式,一种是同步调用,那就是简单的在js绑定函数处调用上一步的curl_request。另一种是异步调用方式,利用libuv,实现异步调用curl_request。
1 static JSValue js_http_get_request(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) 2 { 3 const char* url = JS_ToCString(ctx, argv[0]); 4 JSValueConst func = argv[1]; 5 int sync = JS_ToBool(ctx, argv[2]); 6 if(!JS_IsFunction(ctx, func)){ 7 printf("not a function\n"); 8 return JS_EXCEPTION; 9 } 10 JS_FreeValue(ctx, argv[0]); 11 http_ctx_t *data = calloc(1, sizeof(http_ctx_t)); 12 data->url = url; 13 data->ctx = ctx; 14 data->func = JS_DupValue(ctx, func); 15 if(sync){ 16 printf("c sync req\n"); 17 curl_main(url, data); 18 }else{ 19 printf("c async req\n"); 20 data->handle.data = data; 21 uv_timer_init(loop, &data->handle); 22 uv_timer_start(&data->handle, js_http_get_request_async, 0, 0); 23 } 24 return JS_UNDEFINED; 25 } 26 static const JSCFunctionListEntry js_http_funcs[] = { 27 JS_CFUNC_DEF("get_request", 1, js_http_get_request), 28 }; 29 static int js_http_init(JSContext *ctx, JSModuleDef *m) 30 { 31 return JS_SetModuleExportList(ctx, m, js_http_funcs, countof(js_http_funcs)); 32 } 33 JSModuleDef *js_init_module_http(JSContext *ctx, const char *module_name) 34 { 35 JSModuleDef *m = JS_NewCModule(ctx, module_name, js_http_init); 36 if (!m) 37 return NULL; 38 JS_AddModuleExportList(ctx, m, js_http_funcs, countof(js_http_funcs)); 39 return m; 40 }
三、代码如下
tes_curl.c
1 #include <stdio.h> 2 #include "curl/curl.h" 3 #include "quickjs.h" 4 #include "quickjs-libc.h" 5 #include "uv.h" 6 #include "cutils.h" 7 #include <string.h> 8 9 uv_loop_t *loop; 10 typedef struct _http_ctx_t{ 11 JSContext *ctx; 12 JSValueConst func; 13 uv_timer_t handle; 14 const char * url; 15 } http_ctx_t; 16 17 size_t readData(void *ptr, size_t size, size_t nmemb, void *stream) 18 { 19 http_ctx_t *ctx = (http_ctx_t *)stream; 20 int len = size * nmemb; 21 char buf[10240] = {'\0'}; 22 strncpy(buf, ptr, len); 23 printf("=========================get Data=====================\n"); 24 // printf("%s\n", buf); 25 JSValue func = ctx->func; 26 JSValueConst this_obj = JS_UNDEFINED; 27 JSValueConst argv[1] = {JS_NewString(ctx->ctx, buf)}; 28 JSValue ret = JS_Call(ctx->ctx, func, this_obj, 1, argv); 29 JS_FreeValue(ctx->ctx, func); 30 if(JS_IsException(ret)){ 31 js_std_dump_error(ctx->ctx); 32 } 33 JS_FreeValue(ctx->ctx, argv[0]); 34 JS_FreeValue(ctx->ctx, ret); 35 printf("======================================================\n"); 36 return len; 37 } 38 39 void curl_main(const char * url, http_ctx_t *ctx) 40 { 41 CURL *curl; 42 CURLcode res; 43 curl = curl_easy_init(); 44 45 if (curl) 46 { 47 curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); 48 curl_easy_setopt(curl, CURLOPT_URL, url); 49 50 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); 51 curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "https"); 52 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // 忽略证书检查 53 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); 54 55 struct curl_slist *headers = NULL; 56 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); 57 const char *data = "{}"; 58 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data); 59 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, readData); 60 curl_easy_setopt(curl, CURLOPT_WRITEDATA, ctx); 61 // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); 62 63 res = curl_easy_perform(curl); 64 // printf("res = %d\n", res); 65 if (res != CURLE_OK) 66 fprintf(stderr, "%s\n", curl_easy_strerror(res)); 67 } 68 curl_easy_cleanup(curl); 69 } 70 /*js绑定*/ 71 static void js_http_get_request_async(uv_timer_t *handle) 72 { 73 http_ctx_t *data = (http_ctx_t *)handle->data; 74 curl_main(data->url, data); 75 } 76 static JSValue js_http_get_request(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) 77 { 78 const char* url = JS_ToCString(ctx, argv[0]); 79 JSValueConst func = argv[1]; 80 int sync = JS_ToBool(ctx, argv[2]); 81 if(!JS_IsFunction(ctx, func)){ 82 printf("not a function\n"); 83 return JS_EXCEPTION; 84 } 85 JS_FreeValue(ctx, argv[0]); 86 http_ctx_t *data = calloc(1, sizeof(http_ctx_t)); 87 data->url = url; 88 data->ctx = ctx; 89 data->func = JS_DupValue(ctx, func); 90 if(sync){ 91 printf("c sync req\n"); 92 curl_main(url, data); 93 }else{ 94 printf("c async req\n"); 95 data->handle.data = data; 96 uv_timer_init(loop, &data->handle); 97 uv_timer_start(&data->handle, js_http_get_request_async, 0, 0); 98 } 99 return JS_UNDEFINED; 100 } 101 static const JSCFunctionListEntry js_http_funcs[] = { 102 JS_CFUNC_DEF("get_request", 1, js_http_get_request), 103 }; 104 static int js_http_init(JSContext *ctx, JSModuleDef *m) 105 { 106 return JS_SetModuleExportList(ctx, m, js_http_funcs, countof(js_http_funcs)); 107 } 108 JSModuleDef *js_init_module_http(JSContext *ctx, const char *module_name) 109 { 110 JSModuleDef *m = JS_NewCModule(ctx, module_name, js_http_init); 111 if (!m) 112 return NULL; 113 JS_AddModuleExportList(ctx, m, js_http_funcs, countof(js_http_funcs)); 114 return m; 115 } 116 117 JSContext *js_init_context() 118 { 119 JSRuntime *rt = JS_NewRuntime(); 120 js_std_init_handlers(rt); 121 JSContext *ctx = JS_NewContext(rt); 122 js_init_module_os(ctx, "os"); 123 js_init_module_std(ctx, "std"); 124 js_init_module_http(ctx, "http"); 125 return ctx; 126 } 127 void run_js(JSContext *ctx) 128 { 129 const char *str = 130 "import * as std from 'std'\n" 131 "import * as http from 'http'\n" 132 "import * as os from 'os'\n" 133 "globalThis.std = std\n" 134 "globalThis.http = http\n" 135 "globalThis.os = os\n" 136 "var console = {};\n" 137 "console.log = function(msg) {\n" 138 " std.printf(msg + '\\n');\n" 139 "}\n" 140 "globalThis.console = console;\n"; 141 JSValue init_compile = JS_Eval( 142 ctx, str, strlen(str), "<input>", JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY); 143 js_module_set_import_meta(ctx, init_compile, 1, 1); 144 JSValue init_run = JS_EvalFunction(ctx, init_compile); 145 146 const char *buf = "console.log('start sync req');\n" 147 "var url = 'https://mock.apifox.com/m2/385371-0-default/33329355';\n" 148 "http.get_request(url, function(data){\n" 149 " console.log('sync->>' + data);\n" 150 "}, true);\n" 151 "console.log('end sync req');\n" 152 "console.log('start async req');\n" 153 "var url = 'https://mock.apifox.com/m2/385371-0-default/33329355';\n" 154 "http.get_request(url, function(data){\n" 155 " console.log('async->>' + data);\n" 156 "}, false);\n" 157 "console.log('end async req');" 158 ; 159 JSValue result = JS_Eval(ctx, buf, strlen(buf), "test", JS_EVAL_TYPE_GLOBAL); 160 js_std_loop(ctx); 161 int clen; 162 if (JS_ToInt32(ctx, &clen, result) != 0) 163 { 164 js_std_dump_error(ctx); 165 } 166 printf("exit run_js\n"); 167 } 168 169 int main() 170 { 171 loop = uv_default_loop(); 172 JSContext *ctx = js_init_context(); 173 run_js(ctx); 174 while(uv_run(loop, UV_RUN_NOWAIT)){ 175 ; 176 } 177 JS_FreeContext(ctx); 178 JS_FreeRuntime(JS_GetRuntime(ctx)); 179 printf("Success!\n"); 180 return 0; 181 }
运行结果打印
1 D:\test\quickjs\examples\libuv>bin\main.exe 2 start sync req 3 c sync req 4 =========================get Data===================== 5 sync->>{"msg":"ok"} 6 ====================================================== 7 end sync req 8 start async req 9 c async req 10 end async req 11 exit run_js 12 =========================get Data===================== 13 async->>{"msg":"ok"} 14 ====================================================== 15 QuickJS memory usage -- BigNum 2021-03-27 version, 64-bit, malloc limit: -1 16 17 712 + 0 JSRuntime 18 488 + 0 JSContext 19 80 + 0 JSObject 20 40 + 0 JSString 21 136 + 0 JSFunctionBytecode 22 23 JSObject classes 24 25 NAME COUNT SIZE 26 memory allocated 548 54711 (99.8 per block) 27 memory used 398 31337 (8 overhead, 58.7 average slack) 28 atoms 307 22887 (74.6 per atom) 29 strings 1 46 (46.0 per string) 30 Success!
作者:无脑仔的小明 出处:http://www.cnblogs.com/wunaozai/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 如果文中有什么错误,欢迎指出。以免更多的人被误导。有需要沟通的,可以站内私信,文章留言,或者关注“无脑仔的小明”公众号私信我。一定尽力回答。 |