2 * uclient - ustream based protocol client library - ucode binding
4 * Copyright (C) 2024 Felix Fietkau <nbd@openwrt.org>
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 #include <libubox/uloop.h>
19 #include <libubox/blobmsg.h>
20 #include <ucode/module.h>
23 static uc_resource_type_t
*uc_uclient_type
;
24 static uc_value_t
*registry
;
25 static uc_vm_t
*uc_vm
;
27 struct uc_uclient_priv
{
29 const struct ustream_ssl_ops
*ssl_ops
;
30 struct ustream_ssl_ctx
*ssl_ctx
;
34 static void uc_uclient_register(struct uc_uclient_priv
*ucl
, uc_value_t
*res
, uc_value_t
*cb
)
38 len
= ucv_array_length(registry
);
39 for (i
= 0; i
< len
; i
+= 2)
40 if (!ucv_array_get(registry
, i
))
43 ucv_array_set(registry
, i
, ucv_get(res
));
44 ucv_array_set(registry
, i
+ 1, ucv_get(cb
));
48 static void free_uclient(void *ptr
)
50 struct uclient
*cl
= ptr
;
51 struct uc_uclient_priv
*ucl
;
57 ucv_array_set(registry
, ucl
->idx
, NULL
);
58 ucv_array_set(registry
, ucl
->idx
+ 1, NULL
);
64 uc_uclient_free(uc_vm_t
*vm
, size_t nargs
)
66 struct uclient
**cl
= uc_fn_this("uclient");
75 uc_uclient_ssl_init(uc_vm_t
*vm
, size_t nargs
)
77 struct uclient
*cl
= uc_fn_thisval("uclient");
78 const struct ustream_ssl_ops
*ops
;
79 struct ustream_ssl_ctx
*ctx
;
80 struct uc_uclient_priv
*ucl
;
81 uc_value_t
*args
= uc_fn_arg(0);
90 uclient_http_set_ssl_ctx(cl
, NULL
, NULL
, false);
95 ctx
= uclient_new_ssl_context(&ops
);
102 if ((cur
= ucv_object_get(args
, "cert_file", NULL
)) != NULL
) {
103 const char *str
= ucv_string_get(cur
);
104 if (!str
|| ops
->context_set_crt_file(ctx
, str
))
108 if ((cur
= ucv_object_get(args
, "key_file", NULL
)) != NULL
) {
109 const char *str
= ucv_string_get(cur
);
110 if (!str
|| ops
->context_set_key_file(ctx
, str
))
114 if ((cur
= ucv_object_get(args
, "ca_files", NULL
)) != NULL
) {
117 if (ucv_type(cur
) != UC_ARRAY
)
120 len
= ucv_array_length(cur
);
121 for (size_t i
= 0; i
< len
; i
++) {
122 uc_value_t
*c
= ucv_array_get(cur
, i
);
128 str
= ucv_string_get(c
);
132 ops
->context_add_ca_crt_file(ctx
, str
);
138 if ((cur
= ucv_object_get(args
, "verify", NULL
)) != NULL
)
139 verify
= ucv_is_truish(cur
);
141 ops
->context_set_require_validation(ctx
, verify
);
142 uclient_http_set_ssl_ctx(cl
, ops
, ctx
, verify
);
144 return ucv_boolean_new(true);
147 ops
->context_free(ctx
);
152 uc_uclient_set_timeout(uc_vm_t
*vm
, size_t nargs
)
154 struct uclient
*cl
= uc_fn_thisval("uclient");
155 uc_value_t
*val
= uc_fn_arg(0);
157 if (!cl
|| ucv_type(val
) != UC_INTEGER
)
160 if (uclient_set_timeout(cl
, ucv_int64_get(val
)))
163 return ucv_boolean_new(true);
167 uc_uclient_set_url(uc_vm_t
*vm
, size_t nargs
)
169 struct uclient
*cl
= uc_fn_thisval("uclient");
170 uc_value_t
*url
= uc_fn_arg(0);
171 uc_value_t
*auth_str
= uc_fn_arg(1);
173 if (!cl
|| ucv_type(url
) != UC_STRING
||
174 (auth_str
&& ucv_type(auth_str
) != UC_STRING
))
177 if (uclient_set_url(cl
, ucv_string_get(url
), ucv_string_get(auth_str
)))
180 return ucv_boolean_new(true);
184 uc_uclient_set_proxy_url(uc_vm_t
*vm
, size_t nargs
)
186 struct uclient
*cl
= uc_fn_thisval("uclient");
187 uc_value_t
*url
= uc_fn_arg(0);
188 uc_value_t
*auth_str
= uc_fn_arg(1);
190 if (!cl
|| ucv_type(url
) != UC_STRING
||
191 (auth_str
&& ucv_type(auth_str
) != UC_STRING
))
194 if (uclient_set_proxy_url(cl
, ucv_string_get(url
), ucv_string_get(auth_str
)))
197 return ucv_boolean_new(true);
201 uc_uclient_get_headers(uc_vm_t
*vm
, size_t nargs
)
203 struct uclient
*cl
= uc_fn_thisval("uclient");
204 struct blob_attr
*cur
;
211 ret
= ucv_object_new(uc_vm
);
212 blobmsg_for_each_attr(cur
, cl
->meta
, rem
) {
215 if (blobmsg_type(cur
) != BLOBMSG_TYPE_STRING
)
218 str
= ucv_string_new(blobmsg_get_string(cur
));
219 ucv_object_add(ret
, blobmsg_name(cur
), ucv_get(str
));
226 uc_uclient_connect(uc_vm_t
*vm
, size_t nargs
)
228 struct uclient
*cl
= uc_fn_thisval("uclient");
230 if (!cl
|| uclient_connect(cl
))
233 return ucv_boolean_new(true);
237 uc_uclient_disconnect(uc_vm_t
*vm
, size_t nargs
)
239 struct uclient
*cl
= uc_fn_thisval("uclient");
244 uclient_disconnect(cl
);
246 return ucv_boolean_new(true);
250 uc_uclient_request(uc_vm_t
*vm
, size_t nargs
)
252 struct uclient
*cl
= uc_fn_thisval("uclient");
253 uc_value_t
*type
= uc_fn_arg(0);
254 uc_value_t
*arg
= uc_fn_arg(1);
256 const char *type_str
= ucv_string_get(type
);
258 if (!cl
|| !type_str
)
261 if (uclient_http_set_request_type(cl
, type_str
))
264 uclient_http_reset_headers(cl
);
266 if ((cur
= ucv_object_get(arg
, "headers", NULL
)) != NULL
) {
267 if (ucv_type(cur
) != UC_OBJECT
)
270 ucv_object_foreach(cur
, key
, val
) {
276 if (ucv_type(val
) == UC_STRING
) {
277 uclient_http_set_header(cl
, key
, ucv_string_get(val
));
281 str
= ucv_to_string(uc_vm
, val
);
282 uclient_http_set_header(cl
, key
, str
);
287 if (uclient_request(cl
))
290 return ucv_boolean_new(true);
294 uc_uclient_redirect(uc_vm_t
*vm
, size_t nargs
)
296 struct uclient
*cl
= uc_fn_thisval("uclient");
298 if (!cl
|| uclient_http_redirect(cl
))
301 return ucv_boolean_new(true);
305 uc_uclient_status(uc_vm_t
*vm
, size_t nargs
)
307 struct uclient
*cl
= uc_fn_thisval("uclient");
308 char addr
[INET6_ADDRSTRLEN
];
315 ret
= ucv_object_new(vm
);
316 ucv_object_add(ret
, "eof", ucv_boolean_new(cl
->eof
));
317 ucv_object_add(ret
, "data_eof", ucv_boolean_new(cl
->data_eof
));
318 ucv_object_add(ret
, "status", ucv_int64_new(cl
->status_code
));
319 ucv_object_add(ret
, "redirect", ucv_boolean_new(uclient_http_status_redirect(cl
)));
321 uclient_get_addr(addr
, &port
, &cl
->local_addr
);
322 ucv_object_add(ret
, "local_addr", ucv_get(ucv_string_new(addr
)));
323 ucv_object_add(ret
, "local_port", ucv_get(ucv_int64_new(port
)));
325 uclient_get_addr(addr
, &port
, &cl
->remote_addr
);
326 ucv_object_add(ret
, "remote_addr", ucv_get(ucv_string_new(addr
)));
327 ucv_object_add(ret
, "remote_port", ucv_get(ucv_int64_new(port
)));
333 uc_uclient_read(uc_vm_t
*vm
, size_t nargs
)
335 struct uclient
*cl
= uc_fn_thisval("uclient");
336 size_t len
= ucv_int64_get(uc_fn_arg(0));
337 uc_stringbuf_t
*strbuf
= NULL
;
338 static char buf
[4096];
348 cur
= uclient_read(cl
, buf
, len
);
353 strbuf
= ucv_stringbuf_new();
355 ucv_stringbuf_addstr(strbuf
, buf
, cur
);
362 return ucv_stringbuf_finish(strbuf
);
366 uc_uclient_write(uc_vm_t
*vm
, size_t nargs
)
368 struct uclient
*cl
= uc_fn_thisval("uclient");
373 for (size_t i
= 0; i
< nargs
; i
++)
374 if (ucv_type(uc_fn_arg(i
)) != UC_STRING
)
377 for (size_t i
= 0; i
< nargs
; i
++) {
378 uc_value_t
*cur
= uc_fn_arg(i
);
380 uclient_write(cl
, ucv_string_get(cur
), ucv_string_length(cur
));
383 return ucv_boolean_new(true);
386 static void uc_uclient_cb(struct uclient
*cl
, const char *name
, uc_value_t
*arg
)
388 struct uc_uclient_priv
*ucl
= cl
->priv
;
389 uc_value_t
*cl_res
, *cb
;
392 cb
= ucv_array_get(registry
, ucl
->idx
+ 1);
396 cb
= ucv_object_get(cb
, name
, NULL
);
400 if (!ucv_is_callable(cb
))
403 cl_res
= ucv_array_get(registry
, ucl
->idx
);
404 uc_vm_stack_push(vm
, ucv_get(cl_res
));
405 uc_vm_stack_push(vm
, ucv_get(cb
));
407 uc_vm_stack_push(vm
, ucv_get(arg
));
409 if (uc_vm_call(vm
, true, !!arg
) != EXCEPTION_NONE
) {
411 vm
->exhandler(vm
, &vm
->exception
);
415 ucv_put(uc_vm_stack_pop(vm
));
418 static void uc_cb_data_read(struct uclient
*cl
)
420 uc_uclient_cb(cl
, "data_read", NULL
);
423 static void uc_cb_data_sent(struct uclient
*cl
)
425 uc_uclient_cb(cl
, "data_sent", NULL
);
428 static void uc_cb_data_eof(struct uclient
*cl
)
430 uc_uclient_cb(cl
, "data_eof", NULL
);
433 static void uc_cb_header_done(struct uclient
*cl
)
435 uc_uclient_cb(cl
, "header_done", NULL
);
438 static void uc_cb_error(struct uclient
*cl
, int code
)
440 uc_uclient_cb(cl
, "error", ucv_int64_new(code
));
444 uc_uclient_new(uc_vm_t
*vm
, size_t nargs
)
446 struct uc_uclient_priv
*ucl
;
447 uc_value_t
*url
= uc_fn_arg(0);
448 uc_value_t
*auth_str
= uc_fn_arg(1);
449 uc_value_t
*cb
= uc_fn_arg(2);
450 static bool _init_done
;
461 if (ucv_type(url
) != UC_STRING
||
462 (auth_str
&& ucv_type(auth_str
) != UC_STRING
) ||
463 ucv_type(cb
) != UC_OBJECT
)
466 ucl
= calloc(1, sizeof(*ucl
));
467 if (ucv_object_get(cb
, "data_read", NULL
))
468 ucl
->cb
.data_read
= uc_cb_data_read
;
469 if (ucv_object_get(cb
, "data_sent", NULL
))
470 ucl
->cb
.data_sent
= uc_cb_data_sent
;
471 if (ucv_object_get(cb
, "data_eof", NULL
))
472 ucl
->cb
.data_eof
= uc_cb_data_eof
;
473 if (ucv_object_get(cb
, "header_done", NULL
))
474 ucl
->cb
.header_done
= uc_cb_header_done
;
475 if (ucv_object_get(cb
, "error", NULL
))
476 ucl
->cb
.error
= uc_cb_error
;
478 cl
= uclient_new(ucv_string_get(url
), ucv_string_get(auth_str
), &ucl
->cb
);
485 ret
= ucv_resource_new(uc_uclient_type
, cl
);
486 uc_uclient_register(ucl
, ret
, cb
);
490 static const uc_function_list_t uclient_fns
[] = {
491 { "free", uc_uclient_free
},
492 { "ssl_init", uc_uclient_ssl_init
},
493 { "set_url", uc_uclient_set_url
},
494 { "set_proxy_url", uc_uclient_set_proxy_url
},
495 { "set_timeout", uc_uclient_set_timeout
},
496 { "get_headers", uc_uclient_get_headers
},
498 { "connect", uc_uclient_connect
},
499 { "disconnect", uc_uclient_disconnect
},
500 { "request", uc_uclient_request
},
501 { "redirect", uc_uclient_redirect
},
502 { "status", uc_uclient_status
},
504 { "read", uc_uclient_read
},
505 { "write", uc_uclient_write
},
508 static const uc_function_list_t global_fns
[] = {
509 { "new", uc_uclient_new
},
512 void uc_module_init(uc_vm_t
*vm
, uc_value_t
*scope
)
514 uc_uclient_type
= uc_type_declare(vm
, "uclient", uclient_fns
, free_uclient
);
515 registry
= ucv_array_new(vm
);
516 uc_vm_registry_set(vm
, "uclient.registry", registry
);
517 uc_function_list_register(scope
, global_fns
);