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
;
36 static void uc_uclient_register(struct uc_uclient_priv
*ucl
, uc_value_t
*cb
)
40 len
= ucv_array_length(registry
);
41 for (i
= 0; i
< len
; i
++)
42 if (!ucv_array_get(registry
, i
))
45 ucv_array_set(registry
, i
, ucv_get(cb
));
49 static void free_uclient(void *ptr
)
51 struct uclient
*cl
= ptr
;
52 struct uc_uclient_priv
*ucl
;
58 ucv_array_set(registry
, ucl
->idx
, NULL
);
59 ucv_array_set(registry
, ucl
->idx
+ 1, NULL
);
65 uc_uclient_free(uc_vm_t
*vm
, size_t nargs
)
67 struct uclient
**cl
= uc_fn_this("uclient");
76 uc_uclient_ssl_init(uc_vm_t
*vm
, size_t nargs
)
78 struct uclient
*cl
= uc_fn_thisval("uclient");
79 const struct ustream_ssl_ops
*ops
;
80 struct ustream_ssl_ctx
*ctx
;
81 struct uc_uclient_priv
*ucl
;
82 uc_value_t
*args
= uc_fn_arg(0);
91 uclient_http_set_ssl_ctx(cl
, NULL
, NULL
, false);
96 ctx
= uclient_new_ssl_context(&ops
);
103 if ((cur
= ucv_object_get(args
, "cert_file", NULL
)) != NULL
) {
104 const char *str
= ucv_string_get(cur
);
105 if (!str
|| ops
->context_set_crt_file(ctx
, str
))
109 if ((cur
= ucv_object_get(args
, "key_file", NULL
)) != NULL
) {
110 const char *str
= ucv_string_get(cur
);
111 if (!str
|| ops
->context_set_key_file(ctx
, str
))
115 if ((cur
= ucv_object_get(args
, "ca_files", NULL
)) != NULL
) {
118 if (ucv_type(cur
) != UC_ARRAY
)
121 len
= ucv_array_length(cur
);
122 for (size_t i
= 0; i
< len
; i
++) {
123 uc_value_t
*c
= ucv_array_get(cur
, i
);
129 str
= ucv_string_get(c
);
133 ops
->context_add_ca_crt_file(ctx
, str
);
139 if ((cur
= ucv_object_get(args
, "verify", NULL
)) != NULL
)
140 verify
= ucv_is_truish(cur
);
142 ops
->context_set_require_validation(ctx
, verify
);
143 uclient_http_set_ssl_ctx(cl
, ops
, ctx
, verify
);
145 return ucv_boolean_new(true);
148 ops
->context_free(ctx
);
153 uc_uclient_set_timeout(uc_vm_t
*vm
, size_t nargs
)
155 struct uclient
*cl
= uc_fn_thisval("uclient");
156 uc_value_t
*val
= uc_fn_arg(0);
158 if (!cl
|| ucv_type(val
) != UC_INTEGER
)
161 if (uclient_set_timeout(cl
, ucv_int64_get(val
)))
164 return ucv_boolean_new(true);
168 uc_uclient_set_url(uc_vm_t
*vm
, size_t nargs
)
170 struct uclient
*cl
= uc_fn_thisval("uclient");
171 uc_value_t
*url
= uc_fn_arg(0);
172 uc_value_t
*auth_str
= uc_fn_arg(1);
174 if (!cl
|| ucv_type(url
) != UC_STRING
||
175 (auth_str
&& ucv_type(auth_str
) != UC_STRING
))
178 if (uclient_set_url(cl
, ucv_string_get(url
), ucv_string_get(auth_str
)))
181 return ucv_boolean_new(true);
185 uc_uclient_set_proxy_url(uc_vm_t
*vm
, size_t nargs
)
187 struct uclient
*cl
= uc_fn_thisval("uclient");
188 uc_value_t
*url
= uc_fn_arg(0);
189 uc_value_t
*auth_str
= uc_fn_arg(1);
191 if (!cl
|| ucv_type(url
) != UC_STRING
||
192 (auth_str
&& ucv_type(auth_str
) != UC_STRING
))
195 if (uclient_set_proxy_url(cl
, ucv_string_get(url
), ucv_string_get(auth_str
)))
198 return ucv_boolean_new(true);
202 uc_uclient_get_headers(uc_vm_t
*vm
, size_t nargs
)
204 struct uclient
*cl
= uc_fn_thisval("uclient");
205 struct blob_attr
*cur
;
212 ret
= ucv_object_new(uc_vm
);
213 blobmsg_for_each_attr(cur
, cl
->meta
, rem
) {
216 if (blobmsg_type(cur
) != BLOBMSG_TYPE_STRING
)
219 str
= ucv_string_new(blobmsg_get_string(cur
));
220 ucv_object_add(ret
, blobmsg_name(cur
), ucv_get(str
));
227 uc_uclient_connect(uc_vm_t
*vm
, size_t nargs
)
229 struct uclient
*cl
= uc_fn_thisval("uclient");
231 if (!cl
|| uclient_connect(cl
))
234 return ucv_boolean_new(true);
238 uc_uclient_disconnect(uc_vm_t
*vm
, size_t nargs
)
240 struct uclient
*cl
= uc_fn_thisval("uclient");
245 uclient_disconnect(cl
);
247 return ucv_boolean_new(true);
251 __uc_uclient_cb(struct uclient
*cl
, const char *name
, uc_value_t
*arg
)
253 struct uc_uclient_priv
*ucl
= cl
->priv
;
257 cb
= ucv_array_get(registry
, ucl
->idx
);
261 cb
= ucv_object_get(cb
, name
, NULL
);
265 if (!ucv_is_callable(cb
))
268 uc_vm_stack_push(vm
, ucv_get(ucl
->resource
));
269 uc_vm_stack_push(vm
, ucv_get(cb
));
271 uc_vm_stack_push(vm
, ucv_get(arg
));
273 if (uc_vm_call(vm
, true, !!arg
) != EXCEPTION_NONE
) {
275 vm
->exhandler(vm
, &vm
->exception
);
279 return uc_vm_stack_pop(vm
);
283 uc_write_str(struct uclient
*cl
, uc_value_t
*val
)
285 uclient_write(cl
, ucv_string_get(val
), ucv_string_length(val
));
288 static bool uc_cb_data_write(struct uclient
*cl
)
290 struct uc_uclient_priv
*ucl
= cl
->priv
;
295 val
= __uc_uclient_cb(cl
, "get_post_data", ucv_int64_new(ucl
->offset
));
296 if (ucv_type(val
) != UC_STRING
)
299 len
= ucv_string_length(val
);
304 uc_write_str(cl
, val
);
313 uc_uclient_request(uc_vm_t
*vm
, size_t nargs
)
315 struct uclient
*cl
= uc_fn_thisval("uclient");
316 struct uc_uclient_priv
*ucl
;
317 uc_value_t
*type
= uc_fn_arg(0);
318 uc_value_t
*arg
= uc_fn_arg(1);
320 const char *type_str
= ucv_string_get(type
);
322 if (!cl
|| !type_str
)
328 if (uclient_http_set_request_type(cl
, type_str
))
331 uclient_http_reset_headers(cl
);
333 if ((cur
= ucv_object_get(arg
, "headers", NULL
)) != NULL
) {
334 if (ucv_type(cur
) != UC_OBJECT
)
337 ucv_object_foreach(cur
, key
, val
) {
343 if (ucv_type(val
) == UC_STRING
) {
344 uclient_http_set_header(cl
, key
, ucv_string_get(val
));
348 str
= ucv_to_string(uc_vm
, val
);
349 uclient_http_set_header(cl
, key
, str
);
354 if ((cur
= ucv_object_get(arg
, "post_data", NULL
)) != NULL
) {
355 if (ucv_type(cur
) != UC_STRING
)
358 uc_write_str(cl
, cur
);
361 while (uc_cb_data_write(cl
))
362 if (uclient_pending_bytes(cl
, true))
363 return ucv_boolean_new(true);
366 if (uclient_request(cl
))
369 return ucv_boolean_new(true);
373 uc_uclient_redirect(uc_vm_t
*vm
, size_t nargs
)
375 struct uclient
*cl
= uc_fn_thisval("uclient");
377 if (!cl
|| uclient_http_redirect(cl
))
380 return ucv_boolean_new(true);
384 uc_uclient_status(uc_vm_t
*vm
, size_t nargs
)
386 struct uclient
*cl
= uc_fn_thisval("uclient");
387 char addr
[INET6_ADDRSTRLEN
];
394 ret
= ucv_object_new(vm
);
395 ucv_object_add(ret
, "eof", ucv_boolean_new(cl
->eof
));
396 ucv_object_add(ret
, "data_eof", ucv_boolean_new(cl
->data_eof
));
397 ucv_object_add(ret
, "status", ucv_int64_new(cl
->status_code
));
398 ucv_object_add(ret
, "redirect", ucv_boolean_new(uclient_http_status_redirect(cl
)));
400 uclient_get_addr(addr
, &port
, &cl
->local_addr
);
401 ucv_object_add(ret
, "local_addr", ucv_get(ucv_string_new(addr
)));
402 ucv_object_add(ret
, "local_port", ucv_get(ucv_int64_new(port
)));
404 uclient_get_addr(addr
, &port
, &cl
->remote_addr
);
405 ucv_object_add(ret
, "remote_addr", ucv_get(ucv_string_new(addr
)));
406 ucv_object_add(ret
, "remote_port", ucv_get(ucv_int64_new(port
)));
412 uc_uclient_read(uc_vm_t
*vm
, size_t nargs
)
414 struct uclient
*cl
= uc_fn_thisval("uclient");
415 size_t len
= ucv_int64_get(uc_fn_arg(0));
416 uc_stringbuf_t
*strbuf
= NULL
;
417 static char buf
[4096];
427 cur
= uclient_read(cl
, buf
, len
);
432 strbuf
= ucv_stringbuf_new();
434 ucv_stringbuf_addstr(strbuf
, buf
, cur
);
441 return ucv_stringbuf_finish(strbuf
);
445 uc_uclient_cb(struct uclient
*cl
, const char *name
, uc_value_t
*arg
)
447 ucv_put(__uc_uclient_cb(cl
, name
, arg
));
450 static void uc_cb_data_read(struct uclient
*cl
)
452 uc_uclient_cb(cl
, "data_read", NULL
);
455 static void uc_cb_data_sent(struct uclient
*cl
)
457 struct uc_uclient_priv
*ucl
= cl
->priv
;
459 if (ucl
->offset
< 0 || uclient_pending_bytes(cl
, true))
462 while (uc_cb_data_write(cl
))
463 if (uclient_pending_bytes(cl
, true))
470 static void uc_cb_data_eof(struct uclient
*cl
)
472 uc_uclient_cb(cl
, "data_eof", NULL
);
475 static void uc_cb_header_done(struct uclient
*cl
)
477 uc_uclient_cb(cl
, "header_done", NULL
);
480 static void uc_cb_error(struct uclient
*cl
, int code
)
482 uc_uclient_cb(cl
, "error", ucv_int64_new(code
));
486 uc_uclient_new(uc_vm_t
*vm
, size_t nargs
)
488 struct uc_uclient_priv
*ucl
;
489 uc_value_t
*url
= uc_fn_arg(0);
490 uc_value_t
*auth_str
= uc_fn_arg(1);
491 uc_value_t
*cb
= uc_fn_arg(2);
492 static bool _init_done
;
502 if (ucv_type(url
) != UC_STRING
||
503 (auth_str
&& ucv_type(auth_str
) != UC_STRING
) ||
504 ucv_type(cb
) != UC_OBJECT
)
507 ucl
= calloc(1, sizeof(*ucl
));
508 if (ucv_object_get(cb
, "data_read", NULL
))
509 ucl
->cb
.data_read
= uc_cb_data_read
;
510 if (ucv_object_get(cb
, "get_post_data", NULL
))
511 ucl
->cb
.data_sent
= uc_cb_data_sent
;
512 if (ucv_object_get(cb
, "data_eof", NULL
))
513 ucl
->cb
.data_eof
= uc_cb_data_eof
;
514 if (ucv_object_get(cb
, "header_done", NULL
))
515 ucl
->cb
.header_done
= uc_cb_header_done
;
516 if (ucv_object_get(cb
, "error", NULL
))
517 ucl
->cb
.error
= uc_cb_error
;
519 cl
= uclient_new(ucv_string_get(url
), ucv_string_get(auth_str
), &ucl
->cb
);
526 uc_uclient_register(ucl
, cb
);
527 ucl
->resource
= ucv_resource_new(uc_uclient_type
, cl
);
529 return ucl
->resource
;
531 static const uc_function_list_t uclient_fns
[] = {
532 { "free", uc_uclient_free
},
533 { "ssl_init", uc_uclient_ssl_init
},
534 { "set_url", uc_uclient_set_url
},
535 { "set_proxy_url", uc_uclient_set_proxy_url
},
536 { "set_timeout", uc_uclient_set_timeout
},
537 { "get_headers", uc_uclient_get_headers
},
539 { "connect", uc_uclient_connect
},
540 { "disconnect", uc_uclient_disconnect
},
541 { "request", uc_uclient_request
},
542 { "redirect", uc_uclient_redirect
},
543 { "status", uc_uclient_status
},
545 { "read", uc_uclient_read
},
548 static const uc_function_list_t global_fns
[] = {
549 { "new", uc_uclient_new
},
552 void uc_module_init(uc_vm_t
*vm
, uc_value_t
*scope
)
554 uc_uclient_type
= uc_type_declare(vm
, "uclient", uclient_fns
, free_uclient
);
555 registry
= ucv_array_new(vm
);
556 uc_vm_registry_set(vm
, "uclient.registry", registry
);
557 uc_function_list_register(scope
, global_fns
);