1d4afb5ceSopenharmony_ci/* 2d4afb5ceSopenharmony_ci * lws-minimal-http-server-dynamic 3d4afb5ceSopenharmony_ci * 4d4afb5ceSopenharmony_ci * Written in 2010-2019 by Andy Green <andy@warmcat.com> 5d4afb5ceSopenharmony_ci * 6d4afb5ceSopenharmony_ci * This file is made available under the Creative Commons CC0 1.0 7d4afb5ceSopenharmony_ci * Universal Public Domain Dedication. 8d4afb5ceSopenharmony_ci * 9d4afb5ceSopenharmony_ci * This demonstrates a minimal http server that can produce dynamic http 10d4afb5ceSopenharmony_ci * content as well as static content. 11d4afb5ceSopenharmony_ci * 12d4afb5ceSopenharmony_ci * To keep it simple, it serves the static stuff from the subdirectory 13d4afb5ceSopenharmony_ci * "./mount-origin" of the directory it was started in. 14d4afb5ceSopenharmony_ci * 15d4afb5ceSopenharmony_ci * You can change that by changing mount.origin below. 16d4afb5ceSopenharmony_ci */ 17d4afb5ceSopenharmony_ci 18d4afb5ceSopenharmony_ci#include <libwebsockets.h> 19d4afb5ceSopenharmony_ci#include <string.h> 20d4afb5ceSopenharmony_ci#include <signal.h> 21d4afb5ceSopenharmony_ci#include <time.h> 22d4afb5ceSopenharmony_ci 23d4afb5ceSopenharmony_ci/* 24d4afb5ceSopenharmony_ci * Unlike ws, http is a stateless protocol. This pss only exists for the 25d4afb5ceSopenharmony_ci * duration of a single http transaction. With http/1.1 keep-alive and http/2, 26d4afb5ceSopenharmony_ci * that is unrelated to (shorter than) the lifetime of the network connection. 27d4afb5ceSopenharmony_ci */ 28d4afb5ceSopenharmony_cistruct pss { 29d4afb5ceSopenharmony_ci char path[128]; 30d4afb5ceSopenharmony_ci 31d4afb5ceSopenharmony_ci int times; 32d4afb5ceSopenharmony_ci int budget; 33d4afb5ceSopenharmony_ci 34d4afb5ceSopenharmony_ci int content_lines; 35d4afb5ceSopenharmony_ci}; 36d4afb5ceSopenharmony_ci 37d4afb5ceSopenharmony_cistatic int interrupted; 38d4afb5ceSopenharmony_ci 39d4afb5ceSopenharmony_cistatic int 40d4afb5ceSopenharmony_cicallback_dynamic_http(struct lws *wsi, enum lws_callback_reasons reason, 41d4afb5ceSopenharmony_ci void *user, void *in, size_t len) 42d4afb5ceSopenharmony_ci{ 43d4afb5ceSopenharmony_ci struct pss *pss = (struct pss *)user; 44d4afb5ceSopenharmony_ci uint8_t buf[LWS_PRE + 2048], *start = &buf[LWS_PRE], *p = start, 45d4afb5ceSopenharmony_ci *end = &buf[sizeof(buf) - 1]; 46d4afb5ceSopenharmony_ci time_t t; 47d4afb5ceSopenharmony_ci int n; 48d4afb5ceSopenharmony_ci#if defined(LWS_HAVE_CTIME_R) 49d4afb5ceSopenharmony_ci char date[32]; 50d4afb5ceSopenharmony_ci#endif 51d4afb5ceSopenharmony_ci 52d4afb5ceSopenharmony_ci switch (reason) { 53d4afb5ceSopenharmony_ci case LWS_CALLBACK_HTTP: 54d4afb5ceSopenharmony_ci 55d4afb5ceSopenharmony_ci /* 56d4afb5ceSopenharmony_ci * If you want to know the full url path used, you can get it 57d4afb5ceSopenharmony_ci * like this 58d4afb5ceSopenharmony_ci * 59d4afb5ceSopenharmony_ci * n = lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_GET_URI); 60d4afb5ceSopenharmony_ci * 61d4afb5ceSopenharmony_ci * The base path is the first (n - strlen((const char *)in)) 62d4afb5ceSopenharmony_ci * chars in buf. 63d4afb5ceSopenharmony_ci */ 64d4afb5ceSopenharmony_ci 65d4afb5ceSopenharmony_ci /* 66d4afb5ceSopenharmony_ci * In contains the url part after the place the mount was 67d4afb5ceSopenharmony_ci * positioned at, eg, if positioned at "/dyn" and given 68d4afb5ceSopenharmony_ci * "/dyn/mypath", in will contain /mypath 69d4afb5ceSopenharmony_ci */ 70d4afb5ceSopenharmony_ci lws_snprintf(pss->path, sizeof(pss->path), "%s", 71d4afb5ceSopenharmony_ci (const char *)in); 72d4afb5ceSopenharmony_ci 73d4afb5ceSopenharmony_ci lws_get_peer_simple(wsi, (char *)buf, sizeof(buf)); 74d4afb5ceSopenharmony_ci lwsl_notice("%s: HTTP: connection %s, path %s\n", __func__, 75d4afb5ceSopenharmony_ci (const char *)buf, pss->path); 76d4afb5ceSopenharmony_ci 77d4afb5ceSopenharmony_ci /* 78d4afb5ceSopenharmony_ci * Demonstrates how to retreive a urlarg x=value 79d4afb5ceSopenharmony_ci */ 80d4afb5ceSopenharmony_ci 81d4afb5ceSopenharmony_ci { 82d4afb5ceSopenharmony_ci char value[100]; 83d4afb5ceSopenharmony_ci int z = lws_get_urlarg_by_name_safe(wsi, "x", value, 84d4afb5ceSopenharmony_ci sizeof(value) - 1); 85d4afb5ceSopenharmony_ci 86d4afb5ceSopenharmony_ci if (z >= 0) 87d4afb5ceSopenharmony_ci lwsl_hexdump_notice(value, (size_t)z); 88d4afb5ceSopenharmony_ci } 89d4afb5ceSopenharmony_ci 90d4afb5ceSopenharmony_ci /* 91d4afb5ceSopenharmony_ci * prepare and write http headers... with regards to content- 92d4afb5ceSopenharmony_ci * length, there are three approaches: 93d4afb5ceSopenharmony_ci * 94d4afb5ceSopenharmony_ci * - http/1.0 or connection:close: no need, but no pipelining 95d4afb5ceSopenharmony_ci * - http/1.1 or connected:keep-alive 96d4afb5ceSopenharmony_ci * (keep-alive is default for 1.1): content-length required 97d4afb5ceSopenharmony_ci * - http/2: no need, LWS_WRITE_HTTP_FINAL closes the stream 98d4afb5ceSopenharmony_ci * 99d4afb5ceSopenharmony_ci * giving the api below LWS_ILLEGAL_HTTP_CONTENT_LEN instead of 100d4afb5ceSopenharmony_ci * a content length forces the connection response headers to 101d4afb5ceSopenharmony_ci * send back "connection: close", disabling keep-alive. 102d4afb5ceSopenharmony_ci * 103d4afb5ceSopenharmony_ci * If you know the final content-length, it's always OK to give 104d4afb5ceSopenharmony_ci * it and keep-alive can work then if otherwise possible. But 105d4afb5ceSopenharmony_ci * often you don't know it and avoiding having to compute it 106d4afb5ceSopenharmony_ci * at header-time makes life easier at the server. 107d4afb5ceSopenharmony_ci */ 108d4afb5ceSopenharmony_ci if (lws_add_http_common_headers(wsi, HTTP_STATUS_OK, 109d4afb5ceSopenharmony_ci "text/html", 110d4afb5ceSopenharmony_ci LWS_ILLEGAL_HTTP_CONTENT_LEN, /* no content len */ 111d4afb5ceSopenharmony_ci &p, end)) 112d4afb5ceSopenharmony_ci return 1; 113d4afb5ceSopenharmony_ci if (lws_finalize_write_http_header(wsi, start, &p, end)) 114d4afb5ceSopenharmony_ci return 1; 115d4afb5ceSopenharmony_ci 116d4afb5ceSopenharmony_ci pss->times = 0; 117d4afb5ceSopenharmony_ci pss->budget = atoi((char *)in + 1); 118d4afb5ceSopenharmony_ci pss->content_lines = 0; 119d4afb5ceSopenharmony_ci if (!pss->budget) 120d4afb5ceSopenharmony_ci pss->budget = 10; 121d4afb5ceSopenharmony_ci 122d4afb5ceSopenharmony_ci /* write the body separately */ 123d4afb5ceSopenharmony_ci lws_callback_on_writable(wsi); 124d4afb5ceSopenharmony_ci 125d4afb5ceSopenharmony_ci return 0; 126d4afb5ceSopenharmony_ci 127d4afb5ceSopenharmony_ci case LWS_CALLBACK_HTTP_WRITEABLE: 128d4afb5ceSopenharmony_ci 129d4afb5ceSopenharmony_ci if (!pss || pss->times > pss->budget) 130d4afb5ceSopenharmony_ci break; 131d4afb5ceSopenharmony_ci 132d4afb5ceSopenharmony_ci /* 133d4afb5ceSopenharmony_ci * We send a large reply in pieces of around 2KB each. 134d4afb5ceSopenharmony_ci * 135d4afb5ceSopenharmony_ci * For http/1, it's possible to send a large buffer at once, 136d4afb5ceSopenharmony_ci * but lws will malloc() up a temp buffer to hold any data 137d4afb5ceSopenharmony_ci * that the kernel didn't accept in one go. This is expensive 138d4afb5ceSopenharmony_ci * in memory and cpu, so it's better to stage the creation of 139d4afb5ceSopenharmony_ci * the data to be sent each time. 140d4afb5ceSopenharmony_ci * 141d4afb5ceSopenharmony_ci * For http/2, large data frames would block the whole 142d4afb5ceSopenharmony_ci * connection, not just the stream and are not allowed. Lws 143d4afb5ceSopenharmony_ci * will call back on writable when the stream both has transmit 144d4afb5ceSopenharmony_ci * credit and the round-robin fair access for sibling streams 145d4afb5ceSopenharmony_ci * allows it. 146d4afb5ceSopenharmony_ci * 147d4afb5ceSopenharmony_ci * For http/2, we must send the last part with 148d4afb5ceSopenharmony_ci * LWS_WRITE_HTTP_FINAL to close the stream representing 149d4afb5ceSopenharmony_ci * this transaction. 150d4afb5ceSopenharmony_ci */ 151d4afb5ceSopenharmony_ci n = LWS_WRITE_HTTP; 152d4afb5ceSopenharmony_ci if (pss->times == pss->budget) 153d4afb5ceSopenharmony_ci n = LWS_WRITE_HTTP_FINAL; 154d4afb5ceSopenharmony_ci 155d4afb5ceSopenharmony_ci if (!pss->times) { 156d4afb5ceSopenharmony_ci /* 157d4afb5ceSopenharmony_ci * the first time, we print some html title 158d4afb5ceSopenharmony_ci */ 159d4afb5ceSopenharmony_ci t = time(NULL); 160d4afb5ceSopenharmony_ci /* 161d4afb5ceSopenharmony_ci * to work with http/2, we must take care about LWS_PRE 162d4afb5ceSopenharmony_ci * valid behind the buffer we will send. 163d4afb5ceSopenharmony_ci */ 164d4afb5ceSopenharmony_ci p += lws_snprintf((char *)p, lws_ptr_diff_size_t(end, p), "<html>" 165d4afb5ceSopenharmony_ci "<head><meta charset=utf-8 " 166d4afb5ceSopenharmony_ci "http-equiv=\"Content-Language\" " 167d4afb5ceSopenharmony_ci "content=\"en\"/></head><body>" 168d4afb5ceSopenharmony_ci "<img src=\"/libwebsockets.org-logo.svg\">" 169d4afb5ceSopenharmony_ci "<br>Dynamic content for '%s' from mountpoint." 170d4afb5ceSopenharmony_ci "<br>Time: %s<br><br>" 171d4afb5ceSopenharmony_ci "</body></html>", pss->path, 172d4afb5ceSopenharmony_ci#if defined(LWS_HAVE_CTIME_R) 173d4afb5ceSopenharmony_ci ctime_r(&t, date)); 174d4afb5ceSopenharmony_ci#else 175d4afb5ceSopenharmony_ci ctime(&t)); 176d4afb5ceSopenharmony_ci#endif 177d4afb5ceSopenharmony_ci } else { 178d4afb5ceSopenharmony_ci /* 179d4afb5ceSopenharmony_ci * after the first time, we create bulk content. 180d4afb5ceSopenharmony_ci * 181d4afb5ceSopenharmony_ci * Again we take care about LWS_PRE valid behind the 182d4afb5ceSopenharmony_ci * buffer we will send. 183d4afb5ceSopenharmony_ci */ 184d4afb5ceSopenharmony_ci 185d4afb5ceSopenharmony_ci while (lws_ptr_diff(end, p) > 80) 186d4afb5ceSopenharmony_ci p += lws_snprintf((char *)p, lws_ptr_diff_size_t(end, p), 187d4afb5ceSopenharmony_ci "%d.%d: this is some content... ", 188d4afb5ceSopenharmony_ci pss->times, pss->content_lines++); 189d4afb5ceSopenharmony_ci 190d4afb5ceSopenharmony_ci p += lws_snprintf((char *)p, lws_ptr_diff_size_t(end, p), "<br><br>"); 191d4afb5ceSopenharmony_ci } 192d4afb5ceSopenharmony_ci 193d4afb5ceSopenharmony_ci pss->times++; 194d4afb5ceSopenharmony_ci if (lws_write(wsi, (uint8_t *)start, lws_ptr_diff_size_t(p, start), (enum lws_write_protocol)n) != 195d4afb5ceSopenharmony_ci lws_ptr_diff(p, start)) 196d4afb5ceSopenharmony_ci return 1; 197d4afb5ceSopenharmony_ci 198d4afb5ceSopenharmony_ci /* 199d4afb5ceSopenharmony_ci * HTTP/1.0 no keepalive: close network connection 200d4afb5ceSopenharmony_ci * HTTP/1.1 or HTTP1.0 + KA: wait / process next transaction 201d4afb5ceSopenharmony_ci * HTTP/2: stream ended, parent connection remains up 202d4afb5ceSopenharmony_ci */ 203d4afb5ceSopenharmony_ci if (n == LWS_WRITE_HTTP_FINAL) { 204d4afb5ceSopenharmony_ci if (lws_http_transaction_completed(wsi)) 205d4afb5ceSopenharmony_ci return -1; 206d4afb5ceSopenharmony_ci } else 207d4afb5ceSopenharmony_ci lws_callback_on_writable(wsi); 208d4afb5ceSopenharmony_ci 209d4afb5ceSopenharmony_ci return 0; 210d4afb5ceSopenharmony_ci 211d4afb5ceSopenharmony_ci default: 212d4afb5ceSopenharmony_ci break; 213d4afb5ceSopenharmony_ci } 214d4afb5ceSopenharmony_ci 215d4afb5ceSopenharmony_ci return lws_callback_http_dummy(wsi, reason, user, in, len); 216d4afb5ceSopenharmony_ci} 217d4afb5ceSopenharmony_ci 218d4afb5ceSopenharmony_cistatic const struct lws_protocols defprot = 219d4afb5ceSopenharmony_ci { "defprot", lws_callback_http_dummy, 0, 0, 0, NULL, 0 }, protocol = 220d4afb5ceSopenharmony_ci { "http", callback_dynamic_http, sizeof(struct pss), 0, 0, NULL, 0 }; 221d4afb5ceSopenharmony_ci 222d4afb5ceSopenharmony_cistatic const struct lws_protocols *pprotocols[] = { &defprot, &protocol, NULL }; 223d4afb5ceSopenharmony_ci 224d4afb5ceSopenharmony_ci/* override the default mount for /dyn in the URL space */ 225d4afb5ceSopenharmony_ci 226d4afb5ceSopenharmony_cistatic const struct lws_http_mount mount_dyn = { 227d4afb5ceSopenharmony_ci /* .mount_next */ NULL, /* linked-list "next" */ 228d4afb5ceSopenharmony_ci /* .mountpoint */ "/dyn", /* mountpoint URL */ 229d4afb5ceSopenharmony_ci /* .origin */ NULL, /* protocol */ 230d4afb5ceSopenharmony_ci /* .def */ NULL, 231d4afb5ceSopenharmony_ci /* .protocol */ "http", 232d4afb5ceSopenharmony_ci /* .cgienv */ NULL, 233d4afb5ceSopenharmony_ci /* .extra_mimetypes */ NULL, 234d4afb5ceSopenharmony_ci /* .interpret */ NULL, 235d4afb5ceSopenharmony_ci /* .cgi_timeout */ 0, 236d4afb5ceSopenharmony_ci /* .cache_max_age */ 0, 237d4afb5ceSopenharmony_ci /* .auth_mask */ 0, 238d4afb5ceSopenharmony_ci /* .cache_reusable */ 0, 239d4afb5ceSopenharmony_ci /* .cache_revalidate */ 0, 240d4afb5ceSopenharmony_ci /* .cache_intermediaries */ 0, 241d4afb5ceSopenharmony_ci /* .origin_protocol */ LWSMPRO_CALLBACK, /* dynamic */ 242d4afb5ceSopenharmony_ci /* .mountpoint_len */ 4, /* char count */ 243d4afb5ceSopenharmony_ci /* .basic_auth_login_file */ NULL, 244d4afb5ceSopenharmony_ci}; 245d4afb5ceSopenharmony_ci 246d4afb5ceSopenharmony_ci/* default mount serves the URL space from ./mount-origin */ 247d4afb5ceSopenharmony_ci 248d4afb5ceSopenharmony_cistatic const struct lws_http_mount mount = { 249d4afb5ceSopenharmony_ci /* .mount_next */ &mount_dyn, /* linked-list "next" */ 250d4afb5ceSopenharmony_ci /* .mountpoint */ "/", /* mountpoint URL */ 251d4afb5ceSopenharmony_ci /* .origin */ "./mount-origin", /* serve from dir */ 252d4afb5ceSopenharmony_ci /* .def */ "index.html", /* default filename */ 253d4afb5ceSopenharmony_ci /* .protocol */ NULL, 254d4afb5ceSopenharmony_ci /* .cgienv */ NULL, 255d4afb5ceSopenharmony_ci /* .extra_mimetypes */ NULL, 256d4afb5ceSopenharmony_ci /* .interpret */ NULL, 257d4afb5ceSopenharmony_ci /* .cgi_timeout */ 0, 258d4afb5ceSopenharmony_ci /* .cache_max_age */ 0, 259d4afb5ceSopenharmony_ci /* .auth_mask */ 0, 260d4afb5ceSopenharmony_ci /* .cache_reusable */ 0, 261d4afb5ceSopenharmony_ci /* .cache_revalidate */ 0, 262d4afb5ceSopenharmony_ci /* .cache_intermediaries */ 0, 263d4afb5ceSopenharmony_ci /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ 264d4afb5ceSopenharmony_ci /* .mountpoint_len */ 1, /* char count */ 265d4afb5ceSopenharmony_ci /* .basic_auth_login_file */ NULL, 266d4afb5ceSopenharmony_ci}; 267d4afb5ceSopenharmony_ci 268d4afb5ceSopenharmony_civoid sigint_handler(int sig) 269d4afb5ceSopenharmony_ci{ 270d4afb5ceSopenharmony_ci interrupted = 1; 271d4afb5ceSopenharmony_ci} 272d4afb5ceSopenharmony_ci 273d4afb5ceSopenharmony_ciint main(int argc, const char **argv) 274d4afb5ceSopenharmony_ci{ 275d4afb5ceSopenharmony_ci struct lws_context_creation_info info; 276d4afb5ceSopenharmony_ci struct lws_context *context; 277d4afb5ceSopenharmony_ci const char *p; 278d4afb5ceSopenharmony_ci int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE 279d4afb5ceSopenharmony_ci /* for LLL_ verbosity above NOTICE to be built into lws, 280d4afb5ceSopenharmony_ci * lws must have been configured and built with 281d4afb5ceSopenharmony_ci * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ 282d4afb5ceSopenharmony_ci /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ 283d4afb5ceSopenharmony_ci /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ 284d4afb5ceSopenharmony_ci /* | LLL_DEBUG */; 285d4afb5ceSopenharmony_ci 286d4afb5ceSopenharmony_ci signal(SIGINT, sigint_handler); 287d4afb5ceSopenharmony_ci 288d4afb5ceSopenharmony_ci if ((p = lws_cmdline_option(argc, argv, "-d"))) 289d4afb5ceSopenharmony_ci logs = atoi(p); 290d4afb5ceSopenharmony_ci 291d4afb5ceSopenharmony_ci lws_set_log_level(logs, NULL); 292d4afb5ceSopenharmony_ci lwsl_user("LWS minimal http server dynamic | visit http://localhost:7681\n"); 293d4afb5ceSopenharmony_ci 294d4afb5ceSopenharmony_ci memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ 295d4afb5ceSopenharmony_ci info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT | 296d4afb5ceSopenharmony_ci LWS_SERVER_OPTION_EXPLICIT_VHOSTS | 297d4afb5ceSopenharmony_ci LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; 298d4afb5ceSopenharmony_ci 299d4afb5ceSopenharmony_ci /* for testing ah queue, not useful in real world */ 300d4afb5ceSopenharmony_ci if (lws_cmdline_option(argc, argv, "--ah1")) 301d4afb5ceSopenharmony_ci info.max_http_header_pool = 1; 302d4afb5ceSopenharmony_ci 303d4afb5ceSopenharmony_ci context = lws_create_context(&info); 304d4afb5ceSopenharmony_ci if (!context) { 305d4afb5ceSopenharmony_ci lwsl_err("lws init failed\n"); 306d4afb5ceSopenharmony_ci return 1; 307d4afb5ceSopenharmony_ci } 308d4afb5ceSopenharmony_ci 309d4afb5ceSopenharmony_ci /* http on 7681 */ 310d4afb5ceSopenharmony_ci 311d4afb5ceSopenharmony_ci info.port = 7681; 312d4afb5ceSopenharmony_ci info.pprotocols = pprotocols; 313d4afb5ceSopenharmony_ci info.mounts = &mount; 314d4afb5ceSopenharmony_ci info.vhost_name = "http"; 315d4afb5ceSopenharmony_ci 316d4afb5ceSopenharmony_ci if (!lws_create_vhost(context, &info)) { 317d4afb5ceSopenharmony_ci lwsl_err("Failed to create tls vhost\n"); 318d4afb5ceSopenharmony_ci goto bail; 319d4afb5ceSopenharmony_ci } 320d4afb5ceSopenharmony_ci 321d4afb5ceSopenharmony_ci /* https on 7682 */ 322d4afb5ceSopenharmony_ci 323d4afb5ceSopenharmony_ci info.port = 7682; 324d4afb5ceSopenharmony_ci info.error_document_404 = "/404.html"; 325d4afb5ceSopenharmony_ci#if defined(LWS_WITH_TLS) 326d4afb5ceSopenharmony_ci info.ssl_cert_filepath = "localhost-100y.cert"; 327d4afb5ceSopenharmony_ci info.ssl_private_key_filepath = "localhost-100y.key"; 328d4afb5ceSopenharmony_ci#endif 329d4afb5ceSopenharmony_ci info.vhost_name = "localhost"; 330d4afb5ceSopenharmony_ci 331d4afb5ceSopenharmony_ci if (!lws_create_vhost(context, &info)) { 332d4afb5ceSopenharmony_ci lwsl_err("Failed to create tls vhost\n"); 333d4afb5ceSopenharmony_ci goto bail; 334d4afb5ceSopenharmony_ci } 335d4afb5ceSopenharmony_ci 336d4afb5ceSopenharmony_ci while (n >= 0 && !interrupted) 337d4afb5ceSopenharmony_ci n = lws_service(context, 0); 338d4afb5ceSopenharmony_ci 339d4afb5ceSopenharmony_cibail: 340d4afb5ceSopenharmony_ci lws_context_destroy(context); 341d4afb5ceSopenharmony_ci 342d4afb5ceSopenharmony_ci return 0; 343d4afb5ceSopenharmony_ci} 344