Project homepage Mailing List  Warmcat.com  API Docs  Github Mirror 
{"schema":"libjg2-1", "vpath":"/git/", "avatar":"/git/avatar/", "alang":"", "gen_ut":1713901974, "reponame":"libwebsockets", "desc":"libwebsockets lightweight C networking library", "owner": { "name": "Andy Green", "email": "andy@warmcat.com", "md5": "c50933ca2aa61e0fe2c43d46bb6b59cb" },"url":"https://libwebsockets.org/repo/libwebsockets", "f":3, "items": [ {"schema":"libjg2-1", "cid":"ae6b61a37e613bdc41ee8016f56d3580", "oid":{ "oid": "f28a45246e7ea479718ddba5e80deb355b23f5f3", "alias": [ "refs/heads/main"]},"blobname": "lib/roles/http/server/server.c", "blob": "/*\n * libwebsockets - small server side websockets and web server implementation\n *\n * Copyright (C) 2010 - 2019 Andy Green \u003candy@warmcat.com\u003e\n *\n * Permission is hereby granted, free of charge, to any person obtaining a copy\n * of this software and associated documentation files (the \u0022Software\u0022), to\n * deal in the Software without restriction, including without limitation the\n * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or\n * sell copies of the Software, and to permit persons to whom the Software is\n * furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included in\n * all copies or substantial portions of the Software.\n *\n * THE SOFTWARE IS PROVIDED \u0022AS IS\u0022, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS\n * IN THE SOFTWARE.\n */\n\n#include \u0022private-lib-core.h\u0022\n\n#if !defined(SOL_TCP) \u0026\u0026 defined(IPPROTO_TCP)\n#define SOL_TCP IPPROTO_TCP\n#endif\n\nconst char * const method_names[] \u003d {\n\t\u0022GET\u0022, \u0022POST\u0022,\n#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)\n\t\u0022OPTIONS\u0022, \u0022PUT\u0022, \u0022PATCH\u0022, \u0022DELETE\u0022,\n#endif\n\t\u0022CONNECT\u0022, \u0022HEAD\u0022,\n#ifdef LWS_WITH_HTTP2\n\t\u0022:path\u0022,\n#endif\n\t};\n\n#if defined(LWS_WITH_FILE_OPS)\nstatic const char * const intermediates[] \u003d { \u0022private\u0022, \u0022public\u0022 };\n#endif\n\n/*\n * return 0: all done\n * 1: nonfatal error\n * \u003c0: fatal error\n *\n * REQUIRES CONTEXT LOCK HELD\n */\n\n#if defined(LWS_WITH_SERVER)\n\nstruct vh_sock_args {\n\tconst struct lws_context_creation_info\t*info;\n\tstruct lws_vhost\t\t\t*vhost;\n\tint\t\t\t\t\taf;\n};\n\n\nstatic int\ncheck_extant(struct lws_dll2 *d, void *user)\n{\n\tstruct lws *wsi \u003d lws_container_of(d, struct lws, listen_list);\n\tstruct vh_sock_args *a \u003d (struct vh_sock_args *)user;\n\n\tif (!lws_vhost_compare_listen(wsi-\u003ea.vhost, a-\u003evhost))\n\t\treturn 0;\n\n\tif (wsi-\u003eaf !\u003d a -\u003eaf)\n\t\treturn 0;\n\n\tif (a-\u003einfo \u0026\u0026 a-\u003einfo-\u003evh_listen_sockfd \u0026\u0026\n\t wsi-\u003edesc.sockfd !\u003d a-\u003einfo-\u003evh_listen_sockfd)\n\t\treturn 0;\n\n\tlwsl_notice(\u0022 using listen skt from vhost %s\u005cn\u0022, wsi-\u003ea.vhost-\u003ename);\n\n\treturn 1;\n}\n\n/*\n * Creates a single listen socket of a specific AF\n */\n\nint\n_lws_vhost_init_server_af(struct vh_sock_args *a)\n{\n\tstruct lws_context *cx \u003d a-\u003evhost-\u003econtext;\n\tstruct lws_context_per_thread *pt;\n\tint n, opt \u003d 1, limit \u003d 1, san \u003d 2;\n\tlws_sockfd_type sockfd;\n\tstruct lws *wsi;\n\tint m \u003d 0, is \u003d 0;\n#if defined(LWS_WITH_IPV6)\n\tint value \u003d 1;\n#endif\n\n\t(void)method_names;\n\t(void)opt;\n\n\tlwsl_info(\u0022%s: af %d\u005cn\u0022, __func__, (int)a-\u003eaf);\n\n\tif (lws_vhost_foreach_listen_wsi(a-\u003evhost-\u003econtext, a, check_extant))\n\t\treturn 0;\n\ndeal:\n\n\tif (!san--)\n\t\treturn -1;\n\n\tif (a-\u003evhost-\u003eiface \u0026\u0026 (!a-\u003einfo || !a-\u003einfo-\u003evh_listen_sockfd)) {\n\n\t\t/*\n\t\t * let's check before we do anything else about the disposition\n\t\t * of the interface he wants to bind to...\n\t\t */\n\t\tis \u003d lws_socket_bind(a-\u003evhost, NULL, LWS_SOCK_INVALID,\n\t\t\t\t a-\u003evhost-\u003elisten_port, a-\u003evhost-\u003eiface,\n\t\t\t\t a-\u003eaf);\n\t\tlwsl_debug(\u0022initial if check says %d\u005cn\u0022, is);\n\n\t\tif (is \u003d\u003d LWS_ITOSA_BUSY)\n\t\t\t/* treat as fatal */\n\t\t\treturn -1;\n\n\t\tlws_start_foreach_llp(struct lws_vhost **, pv,\n\t\t\t\t cx-\u003eno_listener_vhost_list) {\n\t\t\tif (is \u003e\u003d LWS_ITOSA_USABLE \u0026\u0026 *pv \u003d\u003d a-\u003evhost) {\n\t\t\t\t/* on the list and shouldn't be: remove it */\n\t\t\t\tlwsl_debug(\u0022deferred iface: removing vh %s\u005cn\u0022,\n\t\t\t\t\t\t(*pv)-\u003ename);\n\t\t\t\t*pv \u003d a-\u003evhost-\u003eno_listener_vhost_list;\n\t\t\t\ta-\u003evhost-\u003eno_listener_vhost_list \u003d NULL;\n\t\t\t\tgoto done_list;\n\t\t\t}\n\t\t\tif (is \u003c LWS_ITOSA_USABLE \u0026\u0026 *pv \u003d\u003d a-\u003evhost)\n\t\t\t\tgoto done_list;\n\t\t} lws_end_foreach_llp(pv, no_listener_vhost_list);\n\n\t\t/* not on the list... */\n\n\t\tif (is \u003c LWS_ITOSA_USABLE) {\n\n\t\t\t/* ... but needs to be: so add it */\n\n\t\t\tlwsl_debug(\u0022deferred iface: adding vh %s\u005cn\u0022,\n\t\t\t\t\ta-\u003evhost-\u003ename);\n\t\t\ta-\u003evhost-\u003eno_listener_vhost_list \u003d\n\t\t\t\t\tcx-\u003eno_listener_vhost_list;\n\t\t\tcx-\u003eno_listener_vhost_list \u003d a-\u003evhost;\n\t\t}\n\ndone_list:\n\n\t\tswitch (is) {\n\t\tdefault:\n\t\t\tbreak;\n\t\tcase LWS_ITOSA_NOT_EXIST:\n\t\t\t/* can't add it */\n\t\t\tif (!a-\u003einfo)\n\t\t\t\treturn -1;\n\n\t\t\t/* first time */\n\t\t\tlwsl_err(\u0022%s: VH %s: iface %s port %d DOESN'T EXIST\u005cn\u0022,\n\t\t\t\t __func__, a-\u003evhost-\u003ename, a-\u003evhost-\u003eiface,\n\t\t\t\t a-\u003evhost-\u003elisten_port);\n\n\t\t\treturn (a-\u003einfo-\u003eoptions \u0026\n\t\t\t\tLWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND) \u003d\u003d\n\t\t\t\tLWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND ?\n\t\t\t\t-1 : 1;\n\n\t\tcase LWS_ITOSA_NOT_USABLE:\n\t\t\t/* can't add it */\n\t\t\tif (!a-\u003einfo) /* first time */\n\t\t\t\treturn -1;\n\n\t\t\tlwsl_err(\u0022%s: VH %s: iface %s port %d NOT USABLE\u005cn\u0022,\n\t\t\t\t __func__, a-\u003evhost-\u003ename, a-\u003evhost-\u003eiface,\n\t\t\t\t a-\u003evhost-\u003elisten_port);\n\n\t\t\treturn (a-\u003einfo-\u003eoptions \u0026\n\t\t\t\tLWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND) \u003d\u003d\n\t\t\t\tLWS_SERVER_OPTION_FAIL_UPON_UNABLE_TO_BIND ?\n\t\t\t\t-1 : 1;\n\t\t}\n\t} else {\n\t\tif (a-\u003einfo \u0026\u0026 a-\u003einfo-\u003evh_listen_sockfd) {\n\t\t\ta-\u003evhost-\u003eiface \u003d \u0022inherited\u0022;\n\t\t\ta-\u003evhost-\u003elisten_port \u003d a-\u003einfo-\u003eport;\n\t\t}\n\t}\n\n\t(void)n;\n#if defined(__linux__)\n\t/*\n\t * A Unix domain sockets cannot be bound multiple times, even if we\n\t * set the SO_REUSE* options on.\n\t *\n\t * However on recent linux, each thread is able to independently listen.\n\t *\n\t * So we can assume creating just one listening socket for a multi-\n\t * threaded environment will typically work.\n\t */\n\tif (a-\u003eaf !\u003d AF_UNIX)\n\t\tlimit \u003d cx-\u003ecount_threads;\n#endif\n\n\tfor (m \u003d 0; m \u003c limit; m++) {\n\n\t\tif (a-\u003einfo \u0026\u0026 a-\u003einfo-\u003evh_listen_sockfd)\n\t\t\tsockfd \u003d dup((int)a-\u003einfo-\u003evh_listen_sockfd);\n\t\telse\n\t\t\tsockfd \u003d lws_fi(\u0026a-\u003evhost-\u003efic, \u0022listenskt\u0022) ?\n\t\t\t\t\tLWS_SOCK_INVALID :\n\t\t\t\t\tsocket(a-\u003eaf, SOCK_STREAM, 0);\n\n\t\tif (sockfd \u003d\u003d LWS_SOCK_INVALID) {\n\t\t\tlwsl_err(\u0022ERROR opening socket\u005cn\u0022);\n\t\t\treturn 1;\n\t\t}\n\n#if !defined(LWS_PLAT_FREERTOS)\n#if (defined(WIN32) || defined(_WIN32)) \u0026\u0026 defined(SO_EXCLUSIVEADDRUSE)\n\t\t/*\n\t\t * only accept that we are the only listener on the port\n\t\t * https://msdn.microsoft.com/zh-tw/library/\n\t\t * windows/desktop/ms740621(v\u003dvs.85).aspx\n\t\t *\n\t\t * for lws, to match Linux, we default to exclusive listen\n\t\t */\n\t\tif (!lws_check_opt(a-\u003evhost-\u003eoptions,\n\t\t\t\tLWS_SERVER_OPTION_ALLOW_LISTEN_SHARE)) {\n\t\t\tif (setsockopt(sockfd, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,\n\t\t\t\t (const void *)\u0026opt, sizeof(opt)) \u003c 0) {\n\t\t\t\tlwsl_err(\u0022reuseaddr failed\u005cn\u0022);\n\t\t\t\tcompatible_close(sockfd);\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t} else\n#endif\n\n\t\t/*\n\t\t * allow us to restart even if old sockets in TIME_WAIT\n\t\t */\n\t\tif (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,\n\t\t\t (const void *)\u0026opt, sizeof(opt)) \u003c 0) {\n\t\t\tlwsl_err(\u0022reuseaddr failed\u005cn\u0022);\n\t\t\tcompatible_close(sockfd);\n\t\t\treturn -1;\n\t\t}\n\n#if defined(LWS_WITH_IPV6) \u0026\u0026 defined(IPV6_V6ONLY)\n\t\t/*\n\t\t * If we have an ipv6 listen socket, it only accepts ipv6.\n\t\t *\n\t\t * There will be a separate ipv4 listen socket if that's\n\t\t * enabled.\n\t\t */\n\t\tif (a-\u003eaf \u003d\u003d AF_INET6 \u0026\u0026 (!a-\u003einfo || !a-\u003einfo-\u003evh_listen_sockfd) \u0026\u0026\n\t\t setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY,\n\t\t\t (const void*)\u0026value, sizeof(value)) \u003c 0) {\n\t\t\tlwsl_err(\u0022ipv6 only failed\u005cn\u0022);\n\n\t\t\tcompatible_close(sockfd);\n\t\t\treturn -1;\n\t\t}\n#endif\n\n#if defined(__linux__) \u0026\u0026 defined(SO_REUSEPORT)\n\t\t/* keep coverity happy */\n#if LWS_MAX_SMP \u003e 1\n\t\tn \u003d 1;\n#else\n\t\tn \u003d lws_check_opt(a-\u003evhost-\u003eoptions,\n\t\t\t\t LWS_SERVER_OPTION_ALLOW_LISTEN_SHARE);\n#endif\n\t\tif (n || cx-\u003ecount_threads \u003e 1) /* ... also implied by threads \u003e 1 */\n\t\t\tif (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT,\n\t\t\t\t\t(const void *)\u0026opt, sizeof(opt)) \u003c 0) {\n\t\t\t\tlwsl_err(\u0022reuseport failed\u005cn\u0022);\n\t\t\t\tcompatible_close(sockfd);\n\t\t\t\treturn -1;\n\t\t\t}\n#endif\n#endif\n\t\tlws_plat_set_socket_options(a-\u003evhost, sockfd, 0);\n\n\t\tif (!a-\u003einfo || !a-\u003einfo-\u003evh_listen_sockfd) {\n\t\t\tis \u003d lws_socket_bind(a-\u003evhost, NULL, sockfd,\n\t\t\t\t\t a-\u003evhost-\u003elisten_port,\n\t\t\t\t\t a-\u003evhost-\u003eiface, a-\u003eaf);\n\n\t\t\tif (is \u003d\u003d LWS_ITOSA_BUSY) {\n\t\t\t\t/* treat as fatal */\n\t\t\t\tcompatible_close(sockfd);\n\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\t/*\n\t\t\t * There is a race where the network device may come up and then\n\t\t\t * go away and fail here. So correctly handle unexpected failure\n\t\t\t * here despite we earlier confirmed it.\n\t\t\t */\n\t\t\tif (is \u003c 0) {\n\t\t\t\tlwsl_info(\u0022%s: lws_socket_bind says %d\u005cn\u0022, __func__, is);\n\t\t\t\tcompatible_close(sockfd);\n\t\t\t\tif (a-\u003evhost-\u003eiface)\n\t\t\t\t\tgoto deal;\n\t\t\t\treturn -1;\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * Create the listen wsi and customize it\n\t\t */\n\n\t\tlws_context_lock(cx, __func__);\n\t\twsi \u003d __lws_wsi_create_with_role(cx, m, \u0026role_ops_listen, NULL);\n\t\tlws_context_unlock(cx);\n\t\tif (wsi \u003d\u003d NULL) {\n\t\t\tlwsl_err(\u0022Out of mem\u005cn\u0022);\n\t\t\tgoto bail;\n\t\t}\n\n\t\twsi-\u003eaf \u003d (uint8_t)a-\u003eaf;\n\n#ifdef LWS_WITH_UNIX_SOCK\n\t\tif (!LWS_UNIX_SOCK_ENABLED(a-\u003evhost))\n#endif\n\t\t{\n\t\t\twsi-\u003eunix_skt \u003d 1;\n\t\t\ta-\u003evhost-\u003elisten_port \u003d is;\n\n\t\t\tlwsl_debug(\u0022%s: lws_socket_bind says %d\u005cn\u0022, __func__, is);\n\t\t}\n\n\t\twsi-\u003edesc.sockfd \u003d sockfd;\n\t\twsi-\u003ea.protocol \u003d a-\u003evhost-\u003eprotocols;\n\t\tlws_vhost_bind_wsi(a-\u003evhost, wsi);\n\t\twsi-\u003elistener \u003d 1;\n\n\t\tif (wsi-\u003ea.context-\u003eevent_loop_ops-\u003einit_vhost_listen_wsi)\n\t\t\twsi-\u003ea.context-\u003eevent_loop_ops-\u003einit_vhost_listen_wsi(wsi);\n\n\t\tpt \u003d \u0026cx-\u003ept[m];\n\t\tlws_pt_lock(pt, __func__);\n\n\t\tif (__insert_wsi_socket_into_fds(cx, wsi)) {\n\t\t\tlwsl_notice(\u0022inserting wsi socket into fds failed\u005cn\u0022);\n\t\t\tlws_pt_unlock(pt);\n\t\t\tgoto bail;\n\t\t}\n\n\t\tlws_dll2_add_tail(\u0026wsi-\u003elisten_list, \u0026a-\u003evhost-\u003elisten_wsi);\n\t\tlws_pt_unlock(pt);\n\n#if defined(WIN32) \u0026\u0026 defined(TCP_FASTOPEN)\n\t\tif (a-\u003evhost-\u003efo_listen_queue) {\n\t\t\tint optval \u003d 1;\n\t\t\tif (setsockopt(wsi-\u003edesc.sockfd, IPPROTO_TCP,\n\t\t\t\t TCP_FASTOPEN,\n\t\t\t\t (const char*)\u0026optval, sizeof(optval)) \u003c 0) {\n\t\t\t\tint error \u003d LWS_ERRNO;\n\t\t\t\tlwsl_warn(\u0022%s: TCP_NODELAY failed with error %d\u005cn\u0022,\n\t\t\t\t\t\t__func__, error);\n\t\t\t}\n\t\t}\n#else\n#if defined(TCP_FASTOPEN)\n\t\tif (a-\u003evhost-\u003efo_listen_queue) {\n\t\t\tint qlen \u003d a-\u003evhost-\u003efo_listen_queue;\n\n\t\t\tif (setsockopt(wsi-\u003edesc.sockfd, SOL_TCP, TCP_FASTOPEN,\n\t\t\t\t \u0026qlen, sizeof(qlen)))\n\t\t\t\tlwsl_warn(\u0022%s: TCP_FASTOPEN failed\u005cn\u0022, __func__);\n\t\t}\n#endif\n#endif\n\n\t\tn \u003d listen(wsi-\u003edesc.sockfd, LWS_SOMAXCONN);\n\t\tif (n \u003c 0) {\n\t\t\tlwsl_err(\u0022listen failed with error %d\u005cn\u0022, LWS_ERRNO);\n\t\t\tlws_dll2_remove(\u0026wsi-\u003elisten_list);\n\t\t\t__remove_wsi_socket_from_fds(wsi);\n\t\t\tgoto bail;\n\t\t}\n\n\t\tif (wsi) {\n\t\t\tif (a-\u003einfo \u0026\u0026 a-\u003einfo-\u003evh_listen_sockfd)\n\t\t\t\ta-\u003evhost-\u003elisten_port \u003d a-\u003einfo-\u003eport;\n\n\t\t\t__lws_lc_tag(a-\u003evhost-\u003econtext,\n\t\t\t\t \u0026a-\u003evhost-\u003econtext-\u003elcg[LWSLCG_WSI],\n\t\t\t\t \u0026wsi-\u003elc, \u0022listen|%s|%s|%d\u0022,\n\t\t\t\t a-\u003evhost-\u003ename,\n\t\t\t\t a-\u003evhost-\u003eiface ? a-\u003evhost-\u003eiface : \u0022\u0022,\n\t\t\t\t (int)a-\u003evhost-\u003elisten_port);\n\t\t}\n\n\t} /* for each thread able to independently listen */\n\n\tif (!lws_check_opt(cx-\u003eoptions, LWS_SERVER_OPTION_EXPLICIT_VHOSTS)) {\n#ifdef LWS_WITH_UNIX_SOCK\n\t\tif (a-\u003eaf \u003d\u003d AF_UNIX)\n\t\t\tlwsl_info(\u0022 Listening on \u005c\u0022%s\u005c\u0022\u005cn\u0022, a-\u003evhost-\u003eiface);\n\t\telse\n#endif\n\t\t\tlwsl_info(\u0022 Listening on %s:%d\u005cn\u0022,\n\t\t\t\t\ta-\u003evhost-\u003eiface,\n\t\t\t\t\ta-\u003evhost-\u003elisten_port);\n }\n\n\t// info-\u003eport \u003d vhost-\u003elisten_port;\n\n\treturn 0;\n\nbail:\n\tlwsl_err(\u0022%s: bailing\u005cn\u0022, __func__);\n\tcompatible_close(sockfd);\n\n\treturn -1;\n}\n\n\nint\n_lws_vhost_init_server(const struct lws_context_creation_info *info,\n\t\t struct lws_vhost *vhost)\n{\n\tstruct vh_sock_args a;\n\tint n;\n\n\ta.info \u003d info;\n\ta.vhost \u003d vhost;\n\n\tif (info) {\n\t\tvhost-\u003eiface \u003d info-\u003eiface;\n\t\tvhost-\u003elisten_port \u003d info-\u003eport;\n\t}\n\n\t/* set up our external listening socket we serve on */\n\n\tif (vhost-\u003elisten_port \u003d\u003d CONTEXT_PORT_NO_LISTEN ||\n\t vhost-\u003elisten_port \u003d\u003d CONTEXT_PORT_NO_LISTEN_SERVER)\n\t\treturn 0;\n\n\t/*\n\t * Let's figure out what AF(s) we want this vhost to listen on.\n\t *\n\t * We want AF_UNIX alone if that's what's told\n\t */\n\n#if defined(LWS_WITH_UNIX_SOCK)\n\t/*\n\t * If unix socket, ask for that and we are done\n\t */\n\tif (LWS_UNIX_SOCK_ENABLED(vhost)) {\n\t\ta.af \u003d AF_UNIX;\n\t\tgoto single;\n\t}\n#endif\n\n\t/*\n\t * We may support both ipv4 and ipv6, but get a numeric vhost listen\n\t * iface that is unambiguously ipv4 or ipv6, meaning we can only listen\n\t * for the related AF then.\n\t */\n\n\tif (vhost-\u003eiface) {\n\t\tuint8_t buf[16];\n\t\tint q;\n\n\t\tq \u003d lws_parse_numeric_address(vhost-\u003eiface, buf, sizeof(buf));\n\n\t\tif (q \u003d\u003d 4) {\n\t\t\ta.af \u003d AF_INET;\n\t\t\tgoto single;\n\t\t}\n\n\t\tif (q \u003d\u003d 16) {\n#if defined(LWS_WITH_IPV6)\n\t\t\tif (LWS_IPV6_ENABLED(vhost)) {\n\t\t\t\ta.af \u003d AF_INET6;\n\t\t\t\tgoto single;\n\t\t\t}\n#endif\n\t\t\tlwsl_err(\u0022%s: ipv6 not supported on %s\u005cn\u0022, __func__,\n\t\t\t\t\tvhost-\u003ename);\n\t\t\treturn 1;\n\t\t}\n\t}\n\n\t/*\n\t * ... if we make it here, we would want to listen on AF_INET and\n\t * AF_INET6 unless one or the other is forbidden\n\t */\n\n#if defined(LWS_WITH_IPV6)\n\tif (!(LWS_IPV6_ENABLED(vhost) \u0026\u0026\n\t (vhost-\u003eoptions \u0026 LWS_SERVER_OPTION_IPV6_V6ONLY_MODIFY) \u0026\u0026\n\t (vhost-\u003eoptions \u0026 LWS_SERVER_OPTION_IPV6_V6ONLY_VALUE))) {\n#endif\n\t\ta.af \u003d AF_INET;\n\t\tn \u003d _lws_vhost_init_server_af(\u0026a);\n\t\tif (n)\n\t\t\treturn n;\n\n#if defined(LWS_WITH_IPV6)\n\t}\n\tif (LWS_IPV6_ENABLED(vhost)) {\n\t\ta.af \u003d AF_INET6;\n\t\tgoto single;\n\t}\n#endif\n\n\treturn 0;\n\nsingle:\n\treturn _lws_vhost_init_server_af(\u0026a);\n}\n\n#endif\n\nstruct lws_vhost *\nlws_select_vhost(struct lws_context *context, int port, const char *servername)\n{\n\tstruct lws_vhost *vhost \u003d context-\u003evhost_list;\n\tconst char *p;\n\tint n, colon;\n\n\tn \u003d (int)strlen(servername);\n\tcolon \u003d n;\n\tp \u003d strchr(servername, ':');\n\tif (p)\n\t\tcolon \u003d lws_ptr_diff(p, servername);\n\n\t/* Priotity 1: first try exact matches */\n\n\twhile (vhost) {\n\t\tif (port \u003d\u003d vhost-\u003elisten_port \u0026\u0026\n\t\t !strncmp(vhost-\u003ename, servername, (unsigned int)colon)) {\n\t\t\tlwsl_info(\u0022SNI: Found: %s\u005cn\u0022, servername);\n\t\t\treturn vhost;\n\t\t}\n\t\tvhost \u003d vhost-\u003evhost_next;\n\t}\n\n\t/*\n\t * Priority 2: if no exact matches, try matching *.vhost-name\n\t * unintentional matches are possible but resolve to x.com for *.x.com\n\t * which is reasonable. If exact match exists we already chose it and\n\t * never reach here. SSL will still fail it if the cert doesn't allow\n\t * *.x.com.\n\t */\n\tvhost \u003d context-\u003evhost_list;\n\twhile (vhost) {\n\t\tint m \u003d (int)strlen(vhost-\u003ename);\n\t\tif (port \u0026\u0026 port \u003d\u003d vhost-\u003elisten_port \u0026\u0026\n\t\t m \u003c\u003d (colon - 2) \u0026\u0026\n\t\t servername[colon - m - 1] \u003d\u003d '.' \u0026\u0026\n\t\t !strncmp(vhost-\u003ename, servername + colon - m, (unsigned int)m)) {\n\t\t\tlwsl_info(\u0022SNI: Found %s on wildcard: %s\u005cn\u0022,\n\t\t\t\t servername, vhost-\u003ename);\n\t\t\treturn vhost;\n\t\t}\n\t\tvhost \u003d vhost-\u003evhost_next;\n\t}\n\n\t/* Priority 3: match the first vhost on our port */\n\n\tvhost \u003d context-\u003evhost_list;\n\twhile (vhost) {\n\t\tif (port \u0026\u0026 port \u003d\u003d vhost-\u003elisten_port) {\n\t\t\tlwsl_info(\u0022%s: vhost match to %s based on port %d\u005cn\u0022,\n\t\t\t\t\t__func__, vhost-\u003ename, port);\n\t\t\treturn vhost;\n\t\t}\n\t\tvhost \u003d vhost-\u003evhost_next;\n\t}\n\n\t/* no match */\n\n\treturn NULL;\n}\n\nstatic const struct lws_mimetype {\n\tconst char *extension;\n\tconst char *mimetype;\n} server_mimetypes[] \u003d {\n\t{ \u0022.html\u0022, \u0022text/html\u0022 },\n\t{ \u0022.htm\u0022, \u0022text/html\u0022 },\n\t{ \u0022.js\u0022, \u0022text/javascript\u0022 },\n\t{ \u0022.css\u0022, \u0022text/css\u0022 },\n\t{ \u0022.png\u0022, \u0022image/png\u0022 },\n\t{ \u0022.jpg\u0022, \u0022image/jpeg\u0022 },\n\t{ \u0022.jpeg\u0022, \u0022image/jpeg\u0022 },\n\t{ \u0022.ico\u0022, \u0022image/x-icon\u0022 },\n\t{ \u0022.gif\u0022, \u0022image/gif\u0022 },\n\t{ \u0022.svg\u0022, \u0022image/svg+xml\u0022 },\n\t{ \u0022.ttf\u0022, \u0022application/x-font-ttf\u0022 },\n\t{ \u0022.otf\u0022, \u0022application/font-woff\u0022 },\n\t{ \u0022.woff\u0022, \u0022application/font-woff\u0022 },\n\t{ \u0022.woff2\u0022, \u0022application/font-woff2\u0022 },\n\t{ \u0022.gz\u0022, \u0022application/gzip\u0022 },\n\t{ \u0022.txt\u0022, \u0022text/plain\u0022 },\n\t{ \u0022.xml\u0022, \u0022application/xml\u0022 },\n\t{ \u0022.json\u0022, \u0022application/json\u0022 },\n\t{ \u0022.mjs\u0022, \u0022text/javascript\u0022 },\n};\n\nconst char *\nlws_get_mimetype(const char *file, const struct lws_http_mount *m)\n{\n\tconst struct lws_protocol_vhost_options *pvo;\n\tsize_t n \u003d strlen(file), len, i;\n\tconst char *fallback_mimetype \u003d NULL;\n\tconst struct lws_mimetype *mt;\n\n\t/* prioritize user-defined mimetypes */\n\tfor (pvo \u003d m ? m-\u003eextra_mimetypes : NULL; pvo; pvo \u003d pvo-\u003enext) {\n\t\t/* ie, match anything */\n\t\tif (!fallback_mimetype \u0026\u0026 pvo-\u003ename[0] \u003d\u003d '*') {\n\t\t\tfallback_mimetype \u003d pvo-\u003evalue;\n\t\t\tcontinue;\n\t\t}\n\n\t\tlen \u003d strlen(pvo-\u003ename);\n\t\tif (n \u003e len \u0026\u0026 !strcasecmp(\u0026file[n - len], pvo-\u003ename)) {\n\t\t\tlwsl_info(\u0022%s: match to user mimetype: %s\u005cn\u0022, __func__,\n\t\t\t\t pvo-\u003evalue);\n\t\t\treturn pvo-\u003evalue;\n\t\t}\n\t}\n\n\t/* fallback to server-defined mimetypes */\n\tfor (i \u003d 0; i \u003c LWS_ARRAY_SIZE(server_mimetypes); ++i) {\n\t\tmt \u003d \u0026server_mimetypes[i];\n\n\t\tlen \u003d strlen(mt-\u003eextension);\n\t\tif (n \u003e len \u0026\u0026 !strcasecmp(\u0026file[n - len], mt-\u003eextension)) {\n\t\t\tlwsl_info(\u0022%s: match to server mimetype: %s\u005cn\u0022, __func__,\n\t\t\t\t mt-\u003emimetype);\n\t\t\treturn mt-\u003emimetype;\n\t\t}\n\t}\n\n\t/* fallback to '*' if defined */\n\tif (fallback_mimetype) {\n\t\tlwsl_info(\u0022%s: match to any mimetype: %s\u005cn\u0022, __func__,\n\t\t\t fallback_mimetype);\n\t\treturn fallback_mimetype;\n\t}\n\n\treturn NULL;\n}\n\n#if defined(LWS_WITH_FILE_OPS)\nstatic lws_fop_flags_t\nlws_vfs_prepare_flags(struct lws *wsi)\n{\n\tlws_fop_flags_t f \u003d 0;\n\n\tif (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING))\n\t\treturn f;\n\n\tif (strstr(lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_ACCEPT_ENCODING),\n\t\t \u0022gzip\u0022)) {\n\t\tlwsl_info(\u0022client indicates GZIP is acceptable\u005cn\u0022);\n\t\tf |\u003d LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP;\n\t}\n\n\treturn f;\n}\n\nstatic int\nlws_http_serve(struct lws *wsi, char *uri, const char *origin,\n\t const struct lws_http_mount *m)\n{\n\tconst struct lws_protocol_vhost_options *pvo \u003d m-\u003einterpret;\n\tstruct lws_process_html_args args;\n\tconst char *mimetype;\n#if !defined(_WIN32_WCE)\n\tconst struct lws_plat_file_ops *fops;\n\tconst char *vpath;\n\tlws_fop_flags_t fflags \u003d LWS_O_RDONLY;\n#if defined(WIN32) \u0026\u0026 defined(LWS_HAVE__STAT32I64)\n\tstruct _stat32i64 st;\n#else\n\tstruct stat st;\n#endif\n\tint spin \u003d 0;\n#endif\n\tchar path[256], sym[2048];\n\tunsigned char *p \u003d (unsigned char *)sym + 32 + LWS_PRE, *start \u003d p;\n\tunsigned char *end \u003d p + sizeof(sym) - 32 - LWS_PRE;\n#if !defined(WIN32) \u0026\u0026 !defined(LWS_PLAT_FREERTOS)\n\tsize_t len;\n#endif\n\tint n;\n\n\twsi-\u003ehandling_404 \u003d 0;\n\tif (!wsi-\u003ea.vhost)\n\t\treturn -1;\n\n#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)\n\tif (wsi-\u003ea.vhost-\u003ehttp.error_document_404 \u0026\u0026\n\t !strcmp(uri, wsi-\u003ea.vhost-\u003ehttp.error_document_404))\n\t\twsi-\u003ehandling_404 \u003d 1;\n#endif\n\n\tlws_snprintf(path, sizeof(path) - 1, \u0022%s/%s\u0022, origin, uri);\n\n#if !defined(_WIN32_WCE)\n\n\tfflags |\u003d lws_vfs_prepare_flags(wsi);\n\n\tdo {\n\t\tspin++;\n\t\tfops \u003d lws_vfs_select_fops(wsi-\u003ea.context-\u003efops, path, \u0026vpath);\n\n\t\tif (wsi-\u003ehttp.fop_fd)\n\t\t\tlws_vfs_file_close(\u0026wsi-\u003ehttp.fop_fd);\n\n\t\twsi-\u003ehttp.fop_fd \u003d fops-\u003eLWS_FOP_OPEN(fops, wsi-\u003ea.context-\u003efops,\n\t\t\t\t\t\t\tpath, vpath, \u0026fflags);\n\t\tif (!wsi-\u003ehttp.fop_fd) {\n\t\t\tlwsl_info(\u0022%s: Unable to open '%s': errno %d\u005cn\u0022,\n\t\t\t\t __func__, path, errno);\n\n\t\t\treturn 1;\n\t\t}\n\n\t\t/* if it can't be statted, don't try */\n\t\tif (fflags \u0026 LWS_FOP_FLAG_VIRTUAL)\n\t\t\tbreak;\n#if defined(LWS_PLAT_FREERTOS)\n\t\tbreak;\n#endif\n#if !defined(WIN32)\n\t\tif (fstat(wsi-\u003ehttp.fop_fd-\u003efd, \u0026st)) {\n\t\t\tlwsl_info(\u0022unable to stat %s\u005cn\u0022, path);\n\t\t\tgoto notfound;\n\t\t}\n#else\n#if defined(LWS_HAVE__STAT32I64)\n\t\t{\n\t\t\tWCHAR buf[MAX_PATH];\n\t\t\tMultiByteToWideChar(CP_UTF8, 0, path, -1, buf, LWS_ARRAY_SIZE(buf));\n\t\t\tif (_wstat32i64(buf, \u0026st)) {\n\t\t\t\tlwsl_info(\u0022unable to stat %s\u005cn\u0022, path);\n\t\t\t\tgoto notfound;\n\t\t\t}\n\t\t}\n#else\n\t\tif (stat(path, \u0026st)) {\n\t\t\tlwsl_info(\u0022unable to stat %s\u005cn\u0022, path);\n\t\t\tgoto notfound;\n\t\t}\n#endif\n#endif\n\n\t\twsi-\u003ehttp.fop_fd-\u003emod_time \u003d (uint32_t)st.st_mtime;\n\t\tfflags |\u003d LWS_FOP_FLAG_MOD_TIME_VALID;\n\n#if !defined(WIN32) \u0026\u0026 !defined(LWS_PLAT_FREERTOS)\n\t\tif ((S_IFMT \u0026 st.st_mode) \u003d\u003d S_IFLNK) {\n\t\t\tlen \u003d (size_t)readlink(path, sym, sizeof(sym) - 1);\n\t\t\tif (len) {\n\t\t\t\tlwsl_err(\u0022Failed to read link %s\u005cn\u0022, path);\n\t\t\t\tgoto notfound;\n\t\t\t}\n\t\t\tsym[len] \u003d '\u005c0';\n\t\t\tlwsl_debug(\u0022symlink %s -\u003e %s\u005cn\u0022, path, sym);\n\t\t\tlws_snprintf(path, sizeof(path) - 1, \u0022%s\u0022, sym);\n\t\t}\n#endif\n\t\tif ((S_IFMT \u0026 st.st_mode) \u003d\u003d S_IFDIR) {\n\t\t\tlwsl_debug(\u0022default filename append to dir\u005cn\u0022);\n\t\t\tlws_snprintf(path, sizeof(path) - 1, \u0022%s/%s/%s\u0022,\n\t\t\t\t origin, uri, m-\u003edef ? m-\u003edef : \u0022index.html\u0022);\n\t\t}\n\n\t} while ((S_IFMT \u0026 st.st_mode) !\u003d S_IFREG \u0026\u0026 spin \u003c 5);\n\n\tif (spin \u003d\u003d 5)\n\t\tlwsl_err(\u0022symlink loop %s \u005cn\u0022, path);\n\n\tn \u003d sprintf(sym, \u0022%08llX%08lX\u0022,\n\t\t (unsigned long long)lws_vfs_get_length(wsi-\u003ehttp.fop_fd),\n\t\t (unsigned long)lws_vfs_get_mod_time(wsi-\u003ehttp.fop_fd));\n\n\t/* disable ranges if IF_RANGE token invalid */\n\n\tif (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_IF_RANGE))\n\t\tif (strcmp(sym, lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_IF_RANGE)))\n\t\t\t/* differs - defeat Range: */\n\t\t\twsi-\u003ehttp.ah-\u003efrag_index[WSI_TOKEN_HTTP_RANGE] \u003d 0;\n\n\tif (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_IF_NONE_MATCH)) {\n\t\t/*\n\t\t * he thinks he has some version of it already,\n\t\t * check if the tag matches\n\t\t */\n\t\tif (!strcmp(sym, lws_hdr_simple_ptr(wsi,\n\t\t\t\t\tWSI_TOKEN_HTTP_IF_NONE_MATCH))) {\n\n\t\t\tchar cache_control[50], *cc \u003d \u0022no-store\u0022;\n\t\t\tint cclen \u003d 8;\n\n\t\t\tlwsl_debug(\u0022%s: ETAG match %s %s\u005cn\u0022, __func__,\n\t\t\t\t uri, origin);\n\n\t\t\t/* we don't need to send the payload */\n\t\t\tif (lws_add_http_header_status(wsi,\n\t\t\t\t\tHTTP_STATUS_NOT_MODIFIED, \u0026p, end)) {\n\t\t\t\tlwsl_err(\u0022%s: failed adding not modified\u005cn\u0022,\n\t\t\t\t\t\t__func__);\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\tif (lws_add_http_header_by_token(wsi,\n\t\t\t\t\tWSI_TOKEN_HTTP_ETAG,\n\t\t\t\t\t(unsigned char *)sym, n, \u0026p, end))\n\t\t\t\treturn -1;\n\n\t\t\t/* but we still need to send cache control... */\n\n\t\t\tif (m-\u003ecache_max_age \u0026\u0026 m-\u003ecache_reusable) {\n\t\t\t\tif (!m-\u003ecache_revalidate) {\n\t\t\t\t\tcc \u003d cache_control;\n\t\t\t\t\tcclen \u003d sprintf(cache_control,\n\t\t\t\t\t\t\u0022%s, max-age\u003d%u\u0022,\n\t\t\t\t\t\tintermediates[wsi-\u003ecache_intermediaries],\n\t\t\t\t\t\tm-\u003ecache_max_age);\n\t\t\t\t} else {\n\t\t\t\t\tcc \u003d cache_control;\n cclen \u003d sprintf(cache_control,\n \t\u0022must-revalidate, %s, max-age\u003d%u\u0022,\n intermediates[wsi-\u003ecache_intermediaries],\n m-\u003ecache_max_age);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (lws_add_http_header_by_token(wsi,\n\t\t\t\t\tWSI_TOKEN_HTTP_CACHE_CONTROL,\n\t\t\t\t\t(unsigned char *)cc, cclen, \u0026p, end))\n\t\t\t\treturn -1;\n\n\t\t\tif (lws_finalize_http_header(wsi, \u0026p, end))\n\t\t\t\treturn -1;\n\n\t\t\tn \u003d lws_write(wsi, start, lws_ptr_diff_size_t(p, start),\n\t\t\t\t LWS_WRITE_HTTP_HEADERS |\n\t\t\t\t LWS_WRITE_H2_STREAM_END);\n\t\t\tif (n !\u003d lws_ptr_diff(p, start)) {\n\t\t\t\tlwsl_err(\u0022_write returned %d from %ld\u005cn\u0022, n,\n\t\t\t\t\t (long)(p - start));\n\t\t\t\treturn -1;\n\t\t\t}\n\n\t\t\tlws_vfs_file_close(\u0026wsi-\u003ehttp.fop_fd);\n\n\t\t\tif (lws_http_transaction_completed(wsi))\n\t\t\t\treturn -1;\n\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\tif (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_ETAG,\n\t\t\t(unsigned char *)sym, n, \u0026p, end))\n\t\treturn -1;\n#endif\n\n\tmimetype \u003d lws_get_mimetype(path, m);\n\tif (!mimetype) {\n\t\tlwsl_info(\u0022unknown mimetype for %s\u005cn\u0022, path);\n\t\tif (lws_return_http_status(wsi,\n\t\t\t\tHTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL) ||\n\t\t lws_http_transaction_completed(wsi))\n\t\t\treturn -1;\n\n\t\treturn 0;\n\t}\n\tif (!mimetype[0])\n\t\tlwsl_debug(\u0022sending no mimetype for %s\u005cn\u0022, path);\n\n\twsi-\u003esending_chunked \u003d 0;\n\twsi-\u003einterpreting \u003d 0;\n\n\t/*\n\t * check if this is in the list of file suffixes to be interpreted by\n\t * a protocol\n\t */\n\twhile (pvo) {\n\t\tn \u003d (int)strlen(path);\n\t\tif (n \u003e (int)strlen(pvo-\u003ename) \u0026\u0026\n\t\t !strcmp(\u0026path[(unsigned int)n - strlen(pvo-\u003ename)], pvo-\u003ename)) {\n\t\t\twsi-\u003einterpreting \u003d 1;\n\t\t\tif (!wsi-\u003emux_substream)\n\t\t\t\twsi-\u003esending_chunked \u003d 1;\n\n\t\t\twsi-\u003eprotocol_interpret_idx \u003d (char)(\n\t\t\t\tlws_vhost_name_to_protocol(wsi-\u003ea.vhost,\n\t\t\t\t\t\t\t pvo-\u003evalue) -\n\t\t\t\t\u0026lws_get_vhost(wsi)-\u003eprotocols[0]);\n\n\t\t\tlwsl_debug(\u0022want %s interpreted by %s (pcol is %s)\u005cn\u0022, path,\n\t\t\t\t wsi-\u003ea.vhost-\u003eprotocols[\n\t\t\t\t (int)wsi-\u003eprotocol_interpret_idx].name,\n\t\t\t\t wsi-\u003ea.protocol-\u003ename);\n\t\t\tif (lws_bind_protocol(wsi, \u0026wsi-\u003ea.vhost-\u003eprotocols[\n\t\t\t (int)wsi-\u003eprotocol_interpret_idx], __func__))\n\t\t\t\treturn -1;\n\n\t\t\tif (lws_ensure_user_space(wsi))\n\t\t\t\treturn -1;\n\t\t\tbreak;\n\t\t}\n\t\tpvo \u003d pvo-\u003enext;\n\t}\n\n\tif (wsi-\u003esending_chunked) {\n\t\tif (lws_add_http_header_by_token(wsi,\n\t\t\t\tWSI_TOKEN_HTTP_TRANSFER_ENCODING,\n\t\t\t\t(unsigned char *)\u0022chunked\u0022, 7,\n\t\t\t\t\u0026p, end))\n\t\t\treturn -1;\n\t}\n\n\tif (m-\u003eprotocol) {\n\t\tconst struct lws_protocols *pp \u003d lws_vhost_name_to_protocol(\n\t\t\t\t\t\t wsi-\u003ea.vhost, m-\u003eprotocol);\n\n\t\tif (lws_bind_protocol(wsi, pp, __func__))\n\t\t\treturn -1;\n\t\targs.p \u003d (char *)p;\n\t\targs.max_len \u003d lws_ptr_diff(end, p);\n\t\tif (pp-\u003ecallback(wsi, LWS_CALLBACK_ADD_HEADERS,\n\t\t\t\t\t wsi-\u003euser_space, \u0026args, 0))\n\t\t\treturn -1;\n\t\tp \u003d (unsigned char *)args.p;\n\t}\n\n\t*p \u003d '\u005c0';\n\tn \u003d lws_serve_http_file(wsi, path, mimetype, (char *)start,\n\t\t\t\tlws_ptr_diff(p, start));\n\n\tif (n \u003c 0 || ((n \u003e 0) \u0026\u0026 lws_http_transaction_completed(wsi)))\n\t\treturn -1; /* error or can't reuse connection: close the socket */\n\n\treturn 0;\n\nnotfound:\n\n\treturn 1;\n}\n#endif\n\n#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2)\nconst struct lws_http_mount *\nlws_find_mount(struct lws *wsi, const char *uri_ptr, int uri_len)\n{\n\tconst struct lws_http_mount *hm, *hit \u003d NULL;\n\tint best \u003d 0;\n\n\thm \u003d wsi-\u003ea.vhost-\u003ehttp.mount_list;\n\twhile (hm) {\n\t\tif (uri_len \u003e\u003d hm-\u003emountpoint_len \u0026\u0026\n\t\t !strncmp(uri_ptr, hm-\u003emountpoint, hm-\u003emountpoint_len) \u0026\u0026\n\t\t (uri_ptr[hm-\u003emountpoint_len] \u003d\u003d '\u005c0' ||\n\t\t uri_ptr[hm-\u003emountpoint_len] \u003d\u003d '/' ||\n\t\t hm-\u003emountpoint_len \u003d\u003d 1)\n\t\t ) {\n#if defined(LWS_WITH_SYS_METRICS)\n\t\t\tlws_metrics_tag_wsi_add(wsi, \u0022mnt\u0022, hm-\u003emountpoint);\n#endif\n\n\t\t\tif (hm-\u003eorigin_protocol \u003d\u003d LWSMPRO_NO_MOUNT)\n\t\t\t\treturn NULL;\n\n\t\t\tif (hm-\u003eorigin_protocol \u003d\u003d LWSMPRO_CALLBACK ||\n\t\t\t ((hm-\u003eorigin_protocol \u003d\u003d LWSMPRO_CGI ||\n\t\t\t lws_hdr_total_length(wsi, WSI_TOKEN_GET_URI) ||\n\t\t\t lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI) ||\n#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)\n\t\t\t lws_hdr_total_length(wsi, WSI_TOKEN_PUT_URI) ||\n\t\t\t lws_hdr_total_length(wsi, WSI_TOKEN_PATCH_URI) ||\n\t\t\t lws_hdr_total_length(wsi, WSI_TOKEN_DELETE_URI) ||\n#endif\n\t\t\t lws_hdr_total_length(wsi, WSI_TOKEN_HEAD_URI) ||\n#if defined(LWS_ROLE_H2)\n\t\t\t (wsi-\u003emux_substream \u0026\u0026\n\t\t\t\tlws_hdr_total_length(wsi,\n\t\t\t\t\t\tWSI_TOKEN_HTTP_COLON_PATH)) ||\n#endif\n\t\t\t hm-\u003eprotocol) \u0026\u0026\n\t\t\t hm-\u003emountpoint_len \u003e best)) {\n\t\t\t\tbest \u003d hm-\u003emountpoint_len;\n\t\t\t\thit \u003d hm;\n\t\t\t}\n\t\t}\n\t\thm \u003d hm-\u003emount_next;\n\t}\n\n\treturn hit;\n}\n#endif\n\n#if defined(LWS_WITH_HTTP_BASIC_AUTH) \u0026\u0026 !defined(LWS_PLAT_FREERTOS) \u0026\u0026 defined(LWS_WITH_FILE_OPS)\nstatic int\nlws_find_string_in_file(const char *filename, const char *string, int stringlen)\n{\n\tchar buf[128];\n\tint fd, match \u003d 0, pos \u003d 0, n \u003d 0, hit \u003d 0;\n\n\tfd \u003d lws_open(filename, O_RDONLY);\n\tif (fd \u003c 0) {\n\t\tlwsl_err(\u0022can't open auth file: %s\u005cn\u0022, filename);\n\t\treturn 0;\n\t}\n\n\twhile (1) {\n\t\tif (pos \u003d\u003d n) {\n\t\t\tn \u003d (int)read(fd, buf, sizeof(buf));\n\t\t\tif (n \u003c\u003d 0) {\n\t\t\t\tif (match \u003d\u003d stringlen)\n\t\t\t\t\thit \u003d 1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tpos \u003d 0;\n\t\t}\n\n\t\tif (match \u003d\u003d stringlen) {\n\t\t\tif (buf[pos] \u003d\u003d '\u005cr' || buf[pos] \u003d\u003d '\u005cn') {\n\t\t\t\thit \u003d 1;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tmatch \u003d 0;\n\t\t}\n\n\t\tif (buf[pos] \u003d\u003d string[match])\n\t\t\tmatch++;\n\t\telse\n\t\t\tmatch \u003d 0;\n\n\t\tpos++;\n\t}\n\n\tclose(fd);\n\n\treturn hit;\n}\n#endif\n\n#if defined(LWS_WITH_HTTP_BASIC_AUTH)\n\nint\nlws_unauthorised_basic_auth(struct lws *wsi)\n{\n\tstruct lws_context_per_thread *pt \u003d \u0026wsi-\u003ea.context-\u003ept[(int)wsi-\u003etsi];\n\tunsigned char *start \u003d pt-\u003eserv_buf + LWS_PRE,\n\t\t *p \u003d start, *end \u003d p + 2048;\n\tchar buf[64];\n\tint n;\n\n\t/* no auth... tell him it is required */\n\n\tif (lws_add_http_header_status(wsi, HTTP_STATUS_UNAUTHORIZED, \u0026p, end))\n\t\treturn -1;\n\n\tn \u003d lws_snprintf(buf, sizeof(buf), \u0022Basic realm\u003d\u005c\u0022lwsws\u005c\u0022\u0022);\n\tif (lws_add_http_header_by_token(wsi,\n\t\t\tWSI_TOKEN_HTTP_WWW_AUTHENTICATE,\n\t\t\t(unsigned char *)buf, n, \u0026p, end))\n\t\treturn -1;\n\n\tif (lws_add_http_header_content_length(wsi, 0, \u0026p, end))\n\t\treturn -1;\n\n\tif (lws_finalize_http_header(wsi, \u0026p, end))\n\t\treturn -1;\n\n\tn \u003d lws_write(wsi, start, lws_ptr_diff_size_t(p, start), LWS_WRITE_HTTP_HEADERS |\n\t\t\t\t\t LWS_WRITE_H2_STREAM_END);\n\tif (n \u003c 0)\n\t\treturn -1;\n\n\treturn lws_http_transaction_completed(wsi);\n\n}\n\n#endif\n\nint lws_clean_url(char *p)\n{\n\tif (p[0] \u003d\u003d 'h' \u0026\u0026 p[1] \u003d\u003d 't' \u0026\u0026 p[2] \u003d\u003d 't' \u0026\u0026 p[3] \u003d\u003d 'p') {\n\t\tp +\u003d 4;\n\t\tif (*p \u003d\u003d 's')\n\t\tp++;\n\t\tif (*p \u003d\u003d ':') {\n\t\t\tp++;\n\t\t\tif (*p \u003d\u003d '/')\n\t\t\tp++;\n\t\t}\n\t}\n\n\twhile (*p) {\n\t\tif (p[0] \u003d\u003d '/' \u0026\u0026 p[1] \u003d\u003d '/') {\n\t\t\tchar *p1 \u003d p;\n\t\t\twhile (*p1) {\n\t\t\t\t*p1 \u003d p1[1];\n\t\t\t\tp1++;\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tp++;\n\t}\n\n\treturn 0;\n}\n\nstatic const unsigned char methods[] \u003d {\n\tWSI_TOKEN_GET_URI,\n\tWSI_TOKEN_POST_URI,\n#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)\n\tWSI_TOKEN_OPTIONS_URI,\n\tWSI_TOKEN_PUT_URI,\n\tWSI_TOKEN_PATCH_URI,\n\tWSI_TOKEN_DELETE_URI,\n#endif\n\tWSI_TOKEN_CONNECT,\n\tWSI_TOKEN_HEAD_URI,\n#ifdef LWS_WITH_HTTP2\n\tWSI_TOKEN_HTTP_COLON_PATH,\n#endif\n};\n\nint\nlws_http_get_uri_and_method(struct lws *wsi, char **puri_ptr, int *puri_len)\n{\n\tint n, count \u003d 0;\n\n\tfor (n \u003d 0; n \u003c (int)LWS_ARRAY_SIZE(methods); n++)\n\t\tif (lws_hdr_total_length(wsi, methods[n]))\n\t\t\tcount++;\n\tif (!count) {\n\t\tlwsl_warn(\u0022Missing URI in HTTP request\u005cn\u0022);\n\t\treturn -1;\n\t}\n\n\tif (count !\u003d 1 \u0026\u0026\n\t !((wsi-\u003emux_substream || wsi-\u003eh2_stream_carries_ws)\n#if defined(LWS_ROLE_H2)\n\t\t\t \u0026\u0026\n\t lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_PATH)\n#endif\n\t )) {\n\t\tlwsl_warn(\u0022multiple methods?\u005cn\u0022);\n\t\treturn -1;\n\t}\n\n\tfor (n \u003d 0; n \u003c (int)LWS_ARRAY_SIZE(methods); n++)\n\t\tif (lws_hdr_total_length(wsi, methods[n])) {\n\t\t\t*puri_ptr \u003d lws_hdr_simple_ptr(wsi, methods[n]);\n\t\t\t*puri_len \u003d lws_hdr_total_length(wsi, methods[n]);\n\t\t\treturn n;\n\t\t}\n\n\treturn -1;\n}\n\n#if defined(LWS_WITH_HTTP_BASIC_AUTH)\n\nenum lws_check_basic_auth_results\nlws_check_basic_auth(struct lws *wsi, const char *basic_auth_login_file,\n\t\t unsigned int auth_mode)\n{\n#if defined(LWS_WITH_FILE_OPS)\n\tchar b64[160], plain[(sizeof(b64) * 3) / 4], *pcolon;\n\tint m, ml, fi, bar;\n\n\tif (!basic_auth_login_file \u0026\u0026 auth_mode \u003d\u003d LWSAUTHM_DEFAULT)\n\t\treturn LCBA_CONTINUE;\n\n\t/* Did he send auth? */\n\tml \u003d lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_AUTHORIZATION);\n\tif (!ml)\n\t\treturn LCBA_FAILED_AUTH;\n\n\t/* Disallow fragmentation monkey business */\n\n\tfi \u003d wsi-\u003ehttp.ah-\u003efrag_index[WSI_TOKEN_HTTP_AUTHORIZATION];\n\tif (wsi-\u003ehttp.ah-\u003efrags[fi].nfrag) {\n\t\tlwsl_err(\u0022fragmented basic auth header not allowed\u005cn\u0022);\n\t\treturn LCBA_FAILED_AUTH;\n\t}\n\n\tm \u003d lws_hdr_copy(wsi, b64, sizeof(b64),\n\t\t\t WSI_TOKEN_HTTP_AUTHORIZATION);\n\tif (m \u003c 7) {\n\t\tlwsl_err(\u0022b64 auth too long\u005cn\u0022);\n\t\treturn LCBA_END_TRANSACTION;\n\t}\n\n\tb64[5] \u003d '\u005c0';\n\tif (strcasecmp(b64, \u0022Basic\u0022)) {\n\t\tlwsl_err(\u0022auth missing basic: %s\u005cn\u0022, b64);\n\t\treturn LCBA_END_TRANSACTION;\n\t}\n\n\t/* It'll be like Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l */\n\n\tm \u003d lws_b64_decode_string(b64 + 6, plain, sizeof(plain) - 1);\n\tif (m \u003c 0) {\n\t\tlwsl_err(\u0022plain auth too long\u005cn\u0022);\n\t\treturn LCBA_END_TRANSACTION;\n\t}\n\n\tplain[m] \u003d '\u005c0';\n\tpcolon \u003d strchr(plain, ':');\n\tif (!pcolon) {\n\t\tlwsl_err(\u0022basic auth format broken\u005cn\u0022);\n\t\treturn LCBA_END_TRANSACTION;\n\t}\n\n\tswitch (auth_mode) {\n\tcase LWSAUTHM_DEFAULT:\n\t\tif (lws_find_string_in_file(basic_auth_login_file, plain, m))\n\t\t\tbreak;\n\t\tlwsl_err(\u0022%s: basic auth lookup failed\u005cn\u0022, __func__);\n\t\treturn LCBA_FAILED_AUTH;\n\n\tcase LWSAUTHM_BASIC_AUTH_CALLBACK:\n\t\tbar \u003d wsi-\u003ea.protocol-\u003ecallback(wsi,\n\t\t\t\tLWS_CALLBACK_VERIFY_BASIC_AUTHORIZATION,\n\t\t\t\twsi-\u003euser_space, plain, (unsigned int)m);\n\t\tif (!bar)\n\t\t\treturn LCBA_FAILED_AUTH;\n\t\tbreak;\n\tdefault:\n\t\t/* Invalid auth mode so lets fail all authentication attempts */\n\t\treturn LCBA_FAILED_AUTH;\n\t}\n\n\t/*\n\t * Rewrite WSI_TOKEN_HTTP_AUTHORIZATION so it is just the\n\t * authorized username\n\t */\n\n\t*pcolon \u003d '\u005c0';\n\twsi-\u003ehttp.ah-\u003efrags[fi].len \u003d (uint16_t)lws_ptr_diff_size_t(pcolon, \u0026plain[0]);\n\tpcolon \u003d lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_AUTHORIZATION);\n\tstrncpy(pcolon, plain, (unsigned int)(ml - 1));\n\tpcolon[ml - 1] \u003d '\u005c0';\n\tlwsl_info(\u0022%s: basic auth accepted for %s\u005cn\u0022, __func__,\n\t\t lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_AUTHORIZATION));\n\n\treturn LCBA_CONTINUE;\n#else\n\tif (!basic_auth_login_file \u0026\u0026 auth_mode \u003d\u003d LWSAUTHM_DEFAULT)\n\t\treturn LCBA_CONTINUE;\n\treturn LCBA_FAILED_AUTH;\n#endif\n}\n\n#endif\n\n#if defined(LWS_WITH_HTTP_PROXY)\n/*\n * Set up an onward http proxy connection according to the mount this\n * uri falls under. Notice this can also be starting the proxying of what was\n * originally an incoming h1 upgrade, or an h2 ws \u0022upgrade\u0022.\n */\nint\nlws_http_proxy_start(struct lws *wsi, const struct lws_http_mount *hit,\n\t\t char *uri_ptr, char ws)\n{\n\tchar ads[96], host[96], *pcolon, *pslash, unix_skt \u003d 0;\n\tstruct lws_client_connect_info i;\n\tstruct lws *cwsi;\n\tint n, na;\n\tunsigned int max_http_header_data \u003d wsi-\u003ea.context-\u003emax_http_header_data \u003e 256 ?\n\t\t\t\t\t wsi-\u003ea.context-\u003emax_http_header_data : 256;\n\tchar *rpath \u003d NULL;\n\n#if defined(LWS_ROLE_WS)\n\tif (ws)\n\t\t/*\n\t\t * Neither our inbound ws upgrade request side, nor our onward\n\t\t * ws client connection on our side can bind to the actual\n\t\t * protocol that only the remote inbound side and the remote\n\t\t * onward side understand.\n\t\t *\n\t\t * Instead these are both bound to our built-in \u0022lws-ws-proxy\u0022\n\t\t * protocol, which understands how to proxy between the two\n\t\t * sides.\n\t\t *\n\t\t * We bind the parent, inbound part here and our side of the\n\t\t * onward client connection is bound to the same handler using\n\t\t * the .local_protocol_name.\n\t\t */\n\t\tlws_bind_protocol(wsi, \u0026lws_ws_proxy, __func__);\n#endif\n\tmemset(\u0026i, 0, sizeof(i));\n\ti.context \u003d lws_get_context(wsi);\n\n\tif (hit-\u003eorigin[0] \u003d\u003d '+')\n\t\tunix_skt \u003d 1;\n\n\tpcolon \u003d strchr(hit-\u003eorigin, ':');\n\tpslash \u003d strchr(hit-\u003eorigin, '/');\n\tif (!pslash) {\n\t\tlwsl_err(\u0022Proxy mount origin '%s' must have /\u005cn\u0022, hit-\u003eorigin);\n\t\treturn -1;\n\t}\n\n\tif (unix_skt) {\n\t\tif (!pcolon) {\n\t\t\tlwsl_err(\u0022Proxy mount origin for unix skt must \u0022\n\t\t\t\t \u0022have address delimited by :\u005cn\u0022);\n\n\t\t\treturn -1;\n\t\t}\n\t\tn \u003d lws_ptr_diff(pcolon, hit-\u003eorigin);\n\t\tpslash \u003d pcolon;\n\t} else {\n\t\tif (pcolon \u003e pslash)\n\t\t\tpcolon \u003d NULL;\n\n\t\tif (pcolon)\n\t\t\tn \u003d (int)(pcolon - hit-\u003eorigin);\n\t\telse\n\t\t\tn \u003d (int)(pslash - hit-\u003eorigin);\n\n\t\tif (n \u003e\u003d (int)sizeof(ads) - 2)\n\t\t\tn \u003d sizeof(ads) - 2;\n\t}\n\n\tmemcpy(ads, hit-\u003eorigin, (unsigned int)n);\n\tads[n] \u003d '\u005c0';\n\n\ti.address \u003d ads;\n\ti.port \u003d 80;\n\tif (hit-\u003eorigin_protocol \u003d\u003d LWSMPRO_HTTPS) {\n\t\ti.port \u003d 443;\n\t\ti.ssl_connection \u003d 1;\n\t}\n\tif (pcolon)\n\t\ti.port \u003d atoi(pcolon + 1);\n\n\trpath \u003d lws_malloc(max_http_header_data, __func__);\n\tif (!rpath)\n\t\treturn -1;\n\n\t/* rpath needs cleaning after this... ---\u003e */\n\n\tn \u003d lws_snprintf(rpath, max_http_header_data - 1, \u0022/%s/%s\u0022,\n\t\t\t pslash + 1, uri_ptr + hit-\u003emountpoint_len) - 1;\n\tlws_clean_url(rpath);\n\tn \u003d (int)strlen(rpath);\n\tif (n \u0026\u0026 rpath[n - 1] \u003d\u003d '/')\n\t\tn--;\n\n\tna \u003d lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_URI_ARGS);\n\tif (na) {\n\t\tchar *p;\n\t\tint budg;\n\n\t\tif (!n) /* don't start with the ?... use the first / if so */\n\t\t\tn++;\n\n\t\tp \u003d rpath + n;\n\n\t\tif (na \u003e\u003d (int)max_http_header_data - n - 2) {\n\t\t\tlwsl_info(\u0022%s: query string %d longer \u0022\n\t\t\t\t \u0022than we can handle\u005cn\u0022, __func__,\n\t\t\t\t na);\n\t\t\tlws_free(rpath);\n\t\t\treturn -1;\n\t\t}\n\n\t\t*p++ \u003d '?';\n\t\tbudg \u003d lws_hdr_copy(wsi, p,\n\t\t\t (int)(\u0026rpath[max_http_header_data - 1] - p),\n\t\t\t WSI_TOKEN_HTTP_URI_ARGS);\n\t if (budg \u003e 0)\n\t\t p +\u003d budg;\n\n\t\t*p \u003d '\u005c0';\n\t}\n\n\ti.path \u003d rpath;\n\tlwsl_notice(\u0022%s: proxied path '%s'\u005cn\u0022, __func__, i.path);\n\n\t/* incoming may be h1 or h2... if he sends h1 HOST, use that\n\t * directly, otherwise we must convert h2 :authority to h1\n\t * host */\n\n#if 0\n\ti.host \u003d NULL;\n#if defined(LWS_ROLE_H2)\n\tn \u003d lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_AUTHORITY);\n\tif (n \u003e 0)\n\t\ti.host \u003d lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_AUTHORITY);\n\telse\n#endif\n\t{\n\t\tn \u003d lws_hdr_total_length(wsi, WSI_TOKEN_HOST);\n\t\tif (n \u003e 0)\n\t\t\ti.host \u003d lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST);\n\t}\n#endif\n\ti.host \u003d i.address;\n\n#if 0\n\tif (i.address[0] !\u003d '+' ||\n\t !lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST))\n\t\ti.host \u003d i.address;\n\telse\n\t\ti.host \u003d lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST);\n#endif\n\ti.origin \u003d NULL;\n\tif (!ws) {\n\t\tif (lws_hdr_simple_ptr(wsi, WSI_TOKEN_POST_URI)\n#if defined(LWS_WITH_HTTP2)\n\t\t\t\t\t\t\t\t|| (\n\t\t\tlws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD) \u0026\u0026\n\t\t\t!strcmp(lws_hdr_simple_ptr(wsi,\n\t\t\t\t\tWSI_TOKEN_HTTP_COLON_METHOD), \u0022post\u0022)\n\t\t\t)\n#endif\n\t\t)\n\t\t\ti.method \u003d \u0022POST\u0022;\n\t\telse if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PUT_URI)\n#if defined(LWS_WITH_HTTP2)\n\t\t\t\t\t\t\t\t|| (\n\t\t\tlws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD) \u0026\u0026\n\t\t\t!strcmp(lws_hdr_simple_ptr(wsi,\n\t\t\t\t\tWSI_TOKEN_HTTP_COLON_METHOD), \u0022put\u0022)\n\t\t\t)\n#endif\n\t\t)\n\t\t\ti.method \u003d \u0022PUT\u0022;\n\t\telse if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_PATCH_URI)\n#if defined(LWS_WITH_HTTP2)\n\t\t\t\t\t\t\t\t|| (\n\t\t\tlws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD) \u0026\u0026\n\t\t\t!strcmp(lws_hdr_simple_ptr(wsi,\n\t\t\t\t\tWSI_TOKEN_HTTP_COLON_METHOD), \u0022patch\u0022)\n\t\t\t)\n#endif\n\t\t)\n\t\t\ti.method \u003d \u0022PATCH\u0022;\n\t\telse if (lws_hdr_simple_ptr(wsi, WSI_TOKEN_DELETE_URI)\n#if defined(LWS_WITH_HTTP2)\n\t\t\t\t\t\t\t\t|| (\n\t\t\tlws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD) \u0026\u0026\n\t\t\t!strcmp(lws_hdr_simple_ptr(wsi,\n\t\t\t\t\tWSI_TOKEN_HTTP_COLON_METHOD), \u0022delete\u0022)\n\t\t\t)\n#endif\n\t\t)\n\t\t\ti.method \u003d \u0022DELETE\u0022;\n\t\telse\n\t\t\ti.method \u003d \u0022GET\u0022;\n\t}\n\n\tif (i.host)\n\t\tlws_snprintf(host, sizeof(host), \u0022%s:%u\u0022, i.host,\n\t\t\t\t\twsi-\u003ea.vhost-\u003elisten_port);\n\telse\n\t\tlws_snprintf(host, sizeof(host), \u0022%s:%d\u0022, i.address, i.port);\n\n\ti.host \u003d host;\n\n\ti.alpn \u003d \u0022http/1.1\u0022;\n\ti.parent_wsi \u003d wsi;\n\ti.pwsi \u003d \u0026cwsi;\n#if defined(LWS_ROLE_WS)\n\ti.protocol \u003d lws_hdr_simple_ptr(wsi, WSI_TOKEN_PROTOCOL);\n\tif (ws)\n\t\ti.local_protocol_name \u003d \u0022lws-ws-proxy\u0022;\n#endif\n\n//\ti.uri_replace_from \u003d hit-\u003eorigin;\n//\ti.uri_replace_to \u003d hit-\u003emountpoint;\n\n\tlwsl_info(\u0022proxying to %s port %d url %s, ssl %d, from %s, to %s\u005cn\u0022,\n\t\t i.address, i.port, i.path, i.ssl_connection,\n\t\t i.uri_replace_from, i.uri_replace_to);\n\n\tif (!lws_client_connect_via_info(\u0026i)) {\n\t\tlwsl_err(\u0022proxy connect fail\u005cn\u0022);\n\n\t\t/*\n\t\t * ... we can't do the proxy action, but we can\n\t\t * cleanly return him a 503 and a description\n\t\t */\n\n\t\tlws_return_http_status(wsi,\n\t\t\tHTTP_STATUS_SERVICE_UNAVAILABLE,\n\t\t\t\u0022\u003ch1\u003eService Temporarily Unavailable\u003c/h1\u003e\u0022\n\t\t\t\u0022The server is temporarily unable to service \u0022\n\t\t\t\u0022your request due to maintenance downtime or \u0022\n\t\t\t\u0022capacity problems. Please try again later.\u0022);\n\t\tlws_free(rpath);\n\t\treturn 1;\n\t}\n\tlws_free(rpath);\n\n\tlwsl_info(\u0022%s: setting proxy clientside on %s (parent %s)\u005cn\u0022,\n\t\t __func__, lws_wsi_tag(cwsi), lws_wsi_tag(lws_get_parent(cwsi)));\n\n\tcwsi-\u003ehttp.proxy_clientside \u003d 1;\n\tif (ws) {\n\t\twsi-\u003eproxied_ws_parent \u003d 1;\n\t\tcwsi-\u003eh1_ws_proxied \u003d 1;\n\t\tif (i.protocol) {\n\t\t\tlwsl_debug(\u0022%s: (requesting '%s')\u005cn\u0022,\n\t\t\t\t\t__func__, i.protocol);\n\t\t}\n\t}\n\n\treturn 0;\n}\n#endif\n\n\nstatic const char * const oprot[] \u003d {\n\t\u0022http://\u0022, \u0022https://\u0022\n};\n\n\nstatic int\nlws_http_redirect_hit(struct lws_context_per_thread *pt, struct lws *wsi,\n\t\t const struct lws_http_mount *hit, char *uri_ptr,\n\t\t int uri_len, int *h)\n{\n\tchar *s;\n\tint n;\n\n\t*h \u003d 0;\n\ts \u003d uri_ptr + hit-\u003emountpoint_len;\n\n\t/*\n\t * if we have a mountpoint like https://xxx.com/yyy\n\t * there is an implied / at the end for our purposes since\n\t * we can only mount on a \u0022directory\u0022.\n\t *\n\t * But if we just go with that, the browser cannot understand\n\t * that he is actually looking down one \u0022directory level\u0022, so\n\t * even though we give him /yyy/abc.html he acts like the\n\t * current directory level is /. So relative urls like \u0022x.png\u0022\n\t * wrongly look outside the mountpoint.\n\t *\n\t * Therefore if we didn't come in on a url with an explicit\n\t * / at the end, we must redirect to add it so the browser\n\t * understands he is one \u0022directory level\u0022 down.\n\t */\n\tif ((hit-\u003emountpoint_len \u003e 1 ||\n\t (hit-\u003eorigin_protocol \u003d\u003d LWSMPRO_REDIR_HTTP ||\n\t hit-\u003eorigin_protocol \u003d\u003d LWSMPRO_REDIR_HTTPS)) \u0026\u0026\n\t (*s !\u003d '/' ||\n\t (hit-\u003eorigin_protocol \u003d\u003d LWSMPRO_REDIR_HTTP ||\n\t hit-\u003eorigin_protocol \u003d\u003d LWSMPRO_REDIR_HTTPS)) \u0026\u0026\n\t (hit-\u003eorigin_protocol !\u003d LWSMPRO_CGI \u0026\u0026\n\t hit-\u003eorigin_protocol !\u003d LWSMPRO_CALLBACK)) {\n\t\tunsigned char *start \u003d pt-\u003eserv_buf + LWS_PRE, *p \u003d start,\n\t\t\t *end \u003d p + wsi-\u003ea.context-\u003ept_serv_buf_size -\n\t\t\t\t\tLWS_PRE - 512;\n\n\t\t*h \u003d 1;\n\n\t\tlwsl_info(\u0022Doing 301 '%s' org %s\u005cn\u0022, s, hit-\u003eorigin);\n\n\t\t/* \u003e at start indicates deal with by redirect */\n\t\tif (hit-\u003eorigin_protocol \u003d\u003d LWSMPRO_REDIR_HTTP ||\n\t\t hit-\u003eorigin_protocol \u003d\u003d LWSMPRO_REDIR_HTTPS)\n\t\t\tn \u003d lws_snprintf((char *)end, 256, \u0022%s%s\u0022,\n\t\t\t\t oprot[hit-\u003eorigin_protocol \u0026 1],\n\t\t\t\t hit-\u003eorigin);\n\t\telse {\n\t\t\tif (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {\n#if defined(LWS_ROLE_H2)\n\t\t\t\tif (!lws_hdr_total_length(wsi,\n\t\t\t\t\t\tWSI_TOKEN_HTTP_COLON_AUTHORITY))\n#endif\n\t\t\t\t\tgoto bail_nuke_ah;\n#if defined(LWS_ROLE_H2)\n\t\t\t\tn \u003d lws_snprintf((char *)end, 256,\n\t\t\t\t \u0022%s%s%s/\u0022, oprot[!!lws_is_ssl(wsi)],\n\t\t\t\t lws_hdr_simple_ptr(wsi,\n\t\t\t\t\t\tWSI_TOKEN_HTTP_COLON_AUTHORITY),\n\t\t\t\t uri_ptr);\n#endif\n\t\t\t} else\n\t\t\t\tn \u003d lws_snprintf((char *)end, 256,\n\t\t\t\t \u0022%s%s%s/\u0022, oprot[!!lws_is_ssl(wsi)],\n\t\t\t\t lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST),\n\t\t\t\t uri_ptr);\n\t\t}\n\n\t\tlws_clean_url((char *)end);\n\t\tn \u003d lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,\n\t\t\t\t end, n, \u0026p, end);\n\t\tif ((int)n \u003c 0)\n\t\t\tgoto bail_nuke_ah;\n\n\t\treturn lws_http_transaction_completed(wsi);\n\t}\n\n\treturn 0;\n\nbail_nuke_ah:\n\tlws_header_table_detach(wsi, 1);\n\n\treturn 1;\n}\n\nint\nlws_http_action(struct lws *wsi)\n{\n\tstruct lws_context_per_thread *pt \u003d \u0026wsi-\u003ea.context-\u003ept[(int)wsi-\u003etsi];\n\tint uri_len \u003d 0, meth, m, http_version_len, ha;\n\tconst struct lws_http_mount *hit \u003d NULL;\n\tenum http_version request_version;\n\tstruct lws_process_html_args args;\n\tenum http_conn_type conn_type;\n\tchar content_length_str[32];\n\tchar http_version_str[12];\n\tchar http_conn_str[25];\n\tchar *uri_ptr \u003d NULL;\n#if defined(LWS_WITH_FILE_OPS)\n\tchar *s;\n#endif\n\tunsigned int n;\n\n\tmeth \u003d lws_http_get_uri_and_method(wsi, \u0026uri_ptr, \u0026uri_len);\n\tif (meth \u003c 0 || meth \u003e\u003d (int)LWS_ARRAY_SIZE(method_names))\n\t\tgoto bail_nuke_ah;\n\n\tlws_metrics_tag_wsi_add(wsi, \u0022vh\u0022, wsi-\u003ea.vhost-\u003ename);\n\tlws_metrics_tag_wsi_add(wsi, \u0022meth\u0022, method_names[meth]);\n\n\t/* we insist on absolute paths */\n\n\tif (!uri_ptr || uri_ptr[0] !\u003d '/') {\n\t\tlws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);\n\n\t\tgoto bail_nuke_ah;\n\t}\n\n\tlwsl_info(\u0022Method: '%s' (%d), request for '%s'\u005cn\u0022, method_names[meth],\n\t\t meth, uri_ptr);\n\n\tif (wsi-\u003erole_ops \u0026\u0026\n\t lws_rops_fidx(wsi-\u003erole_ops, LWS_ROPS_check_upgrades))\n\t\tswitch (lws_rops_func_fidx(wsi-\u003erole_ops,\n\t\t\t\t\t LWS_ROPS_check_upgrades).\n\t\t\t\t\t\t\tcheck_upgrades(wsi)) {\n\t\tcase LWS_UPG_RET_DONE:\n\t\t\treturn 0;\n\t\tcase LWS_UPG_RET_CONTINUE:\n\t\t\tbreak;\n\t\tcase LWS_UPG_RET_BAIL:\n\t\t\tgoto bail_nuke_ah;\n\t\t}\n\n\tif (lws_ensure_user_space(wsi))\n\t\tgoto bail_nuke_ah;\n\n\t/* HTTP header had a content length? */\n\n\twsi-\u003ehttp.rx_content_length \u003d 0;\n\twsi-\u003ehttp.content_length_explicitly_zero \u003d 0;\n\tif (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)\n#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)\n\t\t\t||\n\t lws_hdr_total_length(wsi, WSI_TOKEN_PATCH_URI) ||\n\t lws_hdr_total_length(wsi, WSI_TOKEN_PUT_URI)\n#endif\n\t )\n\t\twsi-\u003ehttp.rx_content_length \u003d 100 * 1024 * 1024;\n\n\tif (lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH) \u0026\u0026\n\t lws_hdr_copy(wsi, content_length_str,\n\t\t\t sizeof(content_length_str) - 1,\n\t\t\t WSI_TOKEN_HTTP_CONTENT_LENGTH) \u003e 0) {\n\t\twsi-\u003ehttp.rx_content_remain \u003d wsi-\u003ehttp.rx_content_length \u003d\n\t\t\t\t(lws_filepos_t)atoll(content_length_str);\n\t\tif (!wsi-\u003ehttp.rx_content_length) {\n\t\t\twsi-\u003ehttp.content_length_explicitly_zero \u003d 1;\n\t\t\tlwsl_debug(\u0022%s: explicit 0 content-length\u005cn\u0022, __func__);\n\t\t}\n\t}\n\n\tif (wsi-\u003emux_substream) {\n\t\twsi-\u003ehttp.request_version \u003d HTTP_VERSION_2;\n\t} else {\n\t\t/* http_version? Default to 1.0, override with token: */\n\t\trequest_version \u003d HTTP_VERSION_1_0;\n\n\t\t/* Works for single digit HTTP versions. : */\n\t\thttp_version_len \u003d lws_hdr_total_length(wsi, WSI_TOKEN_HTTP);\n\t\tif (http_version_len \u003e 7 \u0026\u0026\n\t\t lws_hdr_copy(wsi, http_version_str,\n\t\t\t\t sizeof(http_version_str) - 1,\n\t\t\t\t WSI_TOKEN_HTTP) \u003e 0 \u0026\u0026\n\t\t http_version_str[5] \u003d\u003d '1' \u0026\u0026 http_version_str[7] \u003d\u003d '1')\n\t\t\trequest_version \u003d HTTP_VERSION_1_1;\n\n\t\twsi-\u003ehttp.request_version \u003d request_version;\n\n\t\t/* HTTP/1.1 defaults to \u0022keep-alive\u0022, 1.0 to \u0022close\u0022 */\n\t\tif (request_version \u003d\u003d HTTP_VERSION_1_1)\n\t\t\tconn_type \u003d HTTP_CONNECTION_KEEP_ALIVE;\n\t\telse\n\t\t\tconn_type \u003d HTTP_CONNECTION_CLOSE;\n\n\t\t/* Override default if http \u0022Connection:\u0022 header: */\n\t\tif (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECTION) \u0026\u0026\n\t\t lws_hdr_copy(wsi, http_conn_str, sizeof(http_conn_str) - 1,\n\t\t\t\t WSI_TOKEN_CONNECTION) \u003e 0) {\n\t\t\thttp_conn_str[sizeof(http_conn_str) - 1] \u003d '\u005c0';\n\t\t\tif (!strcasecmp(http_conn_str, \u0022keep-alive\u0022))\n\t\t\t\tconn_type \u003d HTTP_CONNECTION_KEEP_ALIVE;\n\t\t\telse\n\t\t\t\tif (!strcasecmp(http_conn_str, \u0022close\u0022))\n\t\t\t\t\tconn_type \u003d HTTP_CONNECTION_CLOSE;\n\t\t}\n\t\twsi-\u003ehttp.conn_type \u003d conn_type;\n\t}\n\n\tn \u003d (unsigned int)wsi-\u003ea.protocol-\u003ecallback(wsi, LWS_CALLBACK_FILTER_HTTP_CONNECTION,\n\t\t\t\t wsi-\u003euser_space, uri_ptr, (unsigned int)uri_len);\n\tif (n) {\n\t\tlwsl_info(\u0022LWS_CALLBACK_HTTP closing\u005cn\u0022);\n\n\t\treturn 1;\n\t}\n\t/*\n\t * if there is content supposed to be coming,\n\t * put a timeout on it having arrived\n\t */\n\tif (!wsi-\u003emux_stream_immortal)\n\t\tlws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,\n\t\t\t\t(int)wsi-\u003ea.context-\u003etimeout_secs);\n#if defined(LWS_WITH_TLS)\n\tif (wsi-\u003etls.redirect_to_https) {\n\t\t/*\n\t\t * We accepted http:// only so we could redirect to\n\t\t * https://, so issue the redirect. Create the redirection\n\t\t * URI from the host: header, and regenerate the path part from\n\t\t * the parsed pieces\n\t\t */\n\t\tunsigned char *start \u003d pt-\u003eserv_buf + LWS_PRE, *p \u003d start,\n\t\t\t *end \u003d p + wsi-\u003ea.context-\u003ept_serv_buf_size -\n\t\t\t\t LWS_PRE;\n\n\t\tn \u003d (unsigned int)lws_hdr_total_length(wsi, WSI_TOKEN_HOST);\n\t\tif (!n || n \u003e 128)\n\t\t\tgoto bail_nuke_ah;\n\n\t\tif (!lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST))\n\t\t\tgoto bail_nuke_ah;\n\n\t\tp +\u003d lws_snprintf((char *)p, lws_ptr_diff_size_t(end, p), \u0022https://\u0022);\n\t\tmemcpy(p, lws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST), n);\n\t\tp +\u003d n;\n\t\t*p++ \u003d '/';\n\t\tif (uri_len \u003e\u003d lws_ptr_diff(end, p))\n\t\t\tgoto bail_nuke_ah;\n\n\t\tif (uri_ptr[0])\n\t\t\tp--;\n\t\tmemcpy(p, uri_ptr, (unsigned int)uri_len);\n\t\tp +\u003d uri_len;\n\n\t\tn \u003d 0;\n\t\twhile (lws_hdr_copy_fragment(wsi, (char *)p + 1,\n\t\t\t\t\t lws_ptr_diff(end, p) - 2,\n\t\t\t\t\t WSI_TOKEN_HTTP_URI_ARGS, (int)n) \u003e 0) {\n\t\t\t*p \u003d n ? '\u0026' : '?';\n\t\t\tp +\u003d strlen((char *)p);\n\t\t\tif (p \u003e\u003d end - 2)\n\t\t\t\tgoto bail_nuke_ah;\n\t\t\tn++;\n\t\t}\n\n\t\tn \u003d (unsigned int)lws_ptr_diff(p, start);\n\n\t\tp +\u003d LWS_PRE;\n\t\tn \u003d (unsigned int)lws_http_redirect(wsi, HTTP_STATUS_MOVED_PERMANENTLY,\n\t\t\t\t start, (int)n, \u0026p, end);\n\t\tif ((int)n \u003c 0)\n\t\t\tgoto bail_nuke_ah;\n\n\t\treturn lws_http_transaction_completed(wsi);\n\t}\n#endif\n\n#ifdef LWS_WITH_ACCESS_LOG\n\tlws_prepare_access_log_info(wsi, uri_ptr, uri_len, meth);\n#endif\n\n\t/* can we serve it from the mount list? */\n\n\twsi-\u003emount_hit \u003d 0;\n\thit \u003d lws_find_mount(wsi, uri_ptr, uri_len);\n\tif (!hit) {\n\t\t/* deferred cleanup and reset to protocols[0] */\n\n\t\tlwsl_info(\u0022no hit\u005cn\u0022);\n\n\t\tif (lws_bind_protocol(wsi, \u0026wsi-\u003ea.vhost-\u003eprotocols[0],\n\t\t\t\t \u0022no mount hit\u0022))\n\t\t\treturn 1;\n\n\t\tlwsi_set_state(wsi, LRS_DOING_TRANSACTION);\n\n\t\tm \u003d wsi-\u003ea.protocol-\u003ecallback(wsi, LWS_CALLBACK_HTTP,\n\t\t\t\t wsi-\u003euser_space, uri_ptr, (unsigned int)uri_len);\n\n\t\tgoto after;\n\t}\n\n\twsi-\u003emount_hit \u003d 1;\n\n#if defined(LWS_WITH_FILE_OPS)\n\ts \u003d uri_ptr + hit-\u003emountpoint_len;\n#endif\n\tn \u003d (unsigned int)lws_http_redirect_hit(pt, wsi, hit, uri_ptr, uri_len, \u0026ha);\n\tif (ha)\n\t\treturn (int)n;\n\n#if defined(LWS_WITH_HTTP_BASIC_AUTH)\n\n\t/* basic auth? */\n\n\tswitch (lws_check_basic_auth(wsi, hit-\u003ebasic_auth_login_file,\n\t\t\t\t hit-\u003eauth_mask \u0026 AUTH_MODE_MASK)) {\n\tcase LCBA_CONTINUE:\n\t\tbreak;\n\tcase LCBA_FAILED_AUTH:\n\t\treturn lws_unauthorised_basic_auth(wsi);\n\tcase LCBA_END_TRANSACTION:\n\t\tlws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL);\n\t\treturn lws_http_transaction_completed(wsi);\n\t}\n#endif\n\n#if defined(LWS_WITH_HTTP_PROXY)\n\t/*\n\t * The mount is a reverse proxy?\n\t */\n\n\t// if (hit)\n\t// lwsl_notice(\u0022%s: origin_protocol: %d\u005cn\u0022, __func__, hit-\u003eorigin_protocol);\n\t//else\n\t//\tlwsl_notice(\u0022%s: no hit\u005cn\u0022, __func__);\n\n\tif (hit-\u003eorigin_protocol \u003d\u003d LWSMPRO_HTTPS ||\n\t hit-\u003eorigin_protocol \u003d\u003d LWSMPRO_HTTP) {\n\t\tn \u003d (unsigned int)lws_http_proxy_start(wsi, hit, uri_ptr, 0);\n\t\t// lwsl_notice(\u0022proxy start says %d\u005cn\u0022, n);\n\t\tif (n)\n\t\t\treturn (int)n;\n\n\t\tgoto deal_body;\n\t}\n#endif\n\n\t/*\n\t * A particular protocol callback is mounted here?\n\t *\n\t * For the duration of this http transaction, bind us to the\n\t * associated protocol\n\t */\n\tif (hit-\u003eorigin_protocol \u003d\u003d LWSMPRO_CALLBACK || hit-\u003eprotocol) {\n\t\tconst struct lws_protocols *pp;\n\t\tconst char *name \u003d hit-\u003eorigin;\n\t\tif (hit-\u003eprotocol)\n\t\t\tname \u003d hit-\u003eprotocol;\n\n\t\tpp \u003d lws_vhost_name_to_protocol(wsi-\u003ea.vhost, name);\n\t\tif (!pp) {\n\t\t\tlwsl_err(\u0022Unable to find plugin '%s'\u005cn\u0022,\n\t\t\t\t name);\n\t\t\treturn 1;\n\t\t}\n\n\t\tif (lws_bind_protocol(wsi, pp, \u0022http action CALLBACK bind\u0022))\n\t\t\treturn 1;\n\n\t\tlwsl_debug(\u0022%s: %s, checking access rights for mask 0x%x\u005cn\u0022,\n\t\t\t\t__func__, hit-\u003eorigin, hit-\u003eauth_mask);\n\n\t\targs.p \u003d uri_ptr;\n\t\targs.len \u003d uri_len;\n\t\targs.max_len \u003d hit-\u003eauth_mask \u0026 ~AUTH_MODE_MASK;\n\t\targs.final \u003d 0; /* used to signal callback dealt with it */\n\t\targs.chunked \u003d 0;\n\n\t\tn \u003d (unsigned int)wsi-\u003ea.protocol-\u003ecallback(wsi,\n\t\t\t\t\t LWS_CALLBACK_CHECK_ACCESS_RIGHTS,\n\t\t\t\t\t wsi-\u003euser_space, \u0026args, 0);\n\t\tif (n) {\n\t\t\tlws_return_http_status(wsi, HTTP_STATUS_UNAUTHORIZED,\n\t\t\t\t\t NULL);\n\t\t\tgoto bail_nuke_ah;\n\t\t}\n\t\tif (args.final) /* callback completely handled it well */\n\t\t\treturn 0;\n\n\t\tif (hit-\u003ecgienv \u0026\u0026 wsi-\u003ea.protocol-\u003ecallback(wsi,\n\t\t\t\tLWS_CALLBACK_HTTP_PMO,\n\t\t\t\twsi-\u003euser_space, (void *)hit-\u003ecgienv, 0))\n\t\t\treturn 1;\n\n\t\tif (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {\n\t\t\tm \u003d wsi-\u003ea.protocol-\u003ecallback(wsi, LWS_CALLBACK_HTTP,\n\t\t\t\t\t wsi-\u003euser_space,\n\t\t\t\t\t uri_ptr + hit-\u003emountpoint_len,\n\t\t\t\t\t (unsigned int)uri_len - hit-\u003emountpoint_len);\n\t\t\tgoto after;\n\t\t}\n\t}\n\n#ifdef LWS_WITH_CGI\n\t/* did we hit something with a cgi:// origin? */\n\tif (hit-\u003eorigin_protocol \u003d\u003d LWSMPRO_CGI) {\n\t\tconst char *cmd[] \u003d {\n\t\t\tNULL, /* replace with cgi path */\n\t\t\tNULL\n\t\t};\n\n\t\tlwsl_debug(\u0022%s: cgi\u005cn\u0022, __func__);\n\t\tcmd[0] \u003d hit-\u003eorigin;\n\n\t\tn \u003d 5;\n\t\tif (hit-\u003ecgi_timeout)\n\t\t\tn \u003d (unsigned int)hit-\u003ecgi_timeout;\n\n\t\tn \u003d (unsigned int)lws_cgi(wsi, cmd, hit-\u003emountpoint_len, (int)n,\n\t\t\t hit-\u003ecgienv);\n\t\tif (n) {\n\t\t\tlwsl_err(\u0022%s: cgi failed\u005cn\u0022, __func__);\n\t\t\treturn -1;\n\t\t}\n\n\t\tgoto deal_body;\n\t}\n#endif\n\n#if defined(LWS_WITH_FILE_OPS)\n\tn \u003d (unsigned int)(uri_len - lws_ptr_diff(s, uri_ptr));\n\tif (s[0] \u003d\u003d '\u005c0' || (n \u003d\u003d 1 \u0026\u0026 s[n - 1] \u003d\u003d '/'))\n\t\ts \u003d (char *)hit-\u003edef;\n\tif (!s)\n\t\ts \u003d \u0022index.html\u0022;\n#endif\n\n\twsi-\u003ecache_secs \u003d (unsigned int)hit-\u003ecache_max_age;\n\twsi-\u003ecache_reuse \u003d hit-\u003ecache_reusable;\n\twsi-\u003ecache_revalidate \u003d hit-\u003ecache_revalidate;\n\twsi-\u003ecache_no \u003d hit-\u003ecache_no;\n\twsi-\u003ecache_intermediaries \u003d hit-\u003ecache_intermediaries;\n\n#if defined(LWS_WITH_FILE_OPS)\n\tm \u003d 1;\n\tif (hit-\u003eorigin_protocol \u003d\u003d LWSMPRO_FILE)\n\t\tm \u003d lws_http_serve(wsi, s, hit-\u003eorigin, hit);\n\n\tif (m \u003e 0)\n#endif\n\t{\n\t\t/*\n\t\t * lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);\n\t\t */\n\t\tif (hit-\u003eprotocol) {\n\t\t\tconst struct lws_protocols *pp \u003d\n\t\t\t\t\tlws_vhost_name_to_protocol(\n\t\t\t\t\t\twsi-\u003ea.vhost, hit-\u003eprotocol);\n\n\t\t\t/* coverity */\n\t\t\tif (!pp)\n\t\t\t\treturn 1;\n\n\t\t\tlwsi_set_state(wsi, LRS_DOING_TRANSACTION);\n\n\t\t\tif (lws_bind_protocol(wsi, pp, \u0022http_action HTTP\u0022))\n\t\t\t\treturn 1;\n\n\t\t\tm \u003d pp-\u003ecallback(wsi, LWS_CALLBACK_HTTP,\n\t\t\t\t\t wsi-\u003euser_space,\n\t\t\t\t\t uri_ptr + hit-\u003emountpoint_len,\n\t\t\t\t\t (size_t)(uri_len - hit-\u003emountpoint_len));\n\t\t} else\n\t\t\tm \u003d wsi-\u003ea.protocol-\u003ecallback(wsi, LWS_CALLBACK_HTTP,\n\t\t\t\t wsi-\u003euser_space, uri_ptr, (size_t)uri_len);\n\t}\n\nafter:\n\tif (m) {\n\t\tlwsl_info(\u0022LWS_CALLBACK_HTTP closing\u005cn\u0022);\n\n\t\treturn 1;\n\t}\n\n#if defined(LWS_WITH_CGI) || defined(LWS_WITH_HTTP_PROXY)\ndeal_body:\n#endif\n\t/*\n\t * If we're not issuing a file, check for content_length or\n\t * HTTP keep-alive. No keep-alive header allocation for\n\t * ISSUING_FILE, as this uses HTTP/1.0.\n\t *\n\t * In any case, return 0 and let lws_read decide how to\n\t * proceed based on state\n\t */\n\tif (lwsi_state(wsi) \u003d\u003d LRS_ISSUING_FILE)\n\t\treturn 0;\n\n\t/* Prepare to read body if we have a content length: */\n\tlwsl_debug(\u0022wsi-\u003ehttp.rx_content_length %lld %d %d\u005cn\u0022,\n\t\t (long long)wsi-\u003ehttp.rx_content_length,\n\t\t wsi-\u003eupgraded_to_http2, wsi-\u003emux_substream);\n\n\tif (wsi-\u003ehttp.content_length_explicitly_zero \u0026\u0026\n\t lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) {\n\n\t\t/*\n\t\t * POST with an explicit content-length of zero\n\t\t *\n\t\t * If we don't give the user code the empty HTTP_BODY callback,\n\t\t * he may become confused to hear the HTTP_BODY_COMPLETION (due\n\t\t * to, eg, instantiation of lws_spa never happened).\n\t\t *\n\t\t * HTTP_BODY_COMPLETION is responsible for sending the result\n\t\t * status code and result body if any, and to do the transaction\n\t\t * complete processing.\n\t\t */\n\t\tif (wsi-\u003ea.protocol-\u003ecallback(wsi, LWS_CALLBACK_HTTP_BODY,\n\t\t\t\t\t wsi-\u003euser_space, NULL, 0))\n\t\t\treturn 1;\n\t\tif (wsi-\u003ea.protocol-\u003ecallback(wsi, LWS_CALLBACK_HTTP_BODY_COMPLETION,\n\t\t\t\t\t wsi-\u003euser_space, NULL, 0))\n\t\t\treturn 1;\n\n\t\treturn 0;\n\t}\n\n\tif (wsi-\u003ehttp.rx_content_length \u003c\u003d 0)\n\t\treturn 0;\n\n\tif (lwsi_state(wsi) !\u003d LRS_DISCARD_BODY) {\n\t\tlwsi_set_state(wsi, LRS_BODY);\n\t\tlwsl_info(\u0022%s: %s: LRS_BODY state set (0x%x)\u005cn\u0022, __func__,\n\t\t\t lws_wsi_tag(wsi), (int)wsi-\u003ewsistate);\n\t}\n\twsi-\u003ehttp.rx_content_remain \u003d wsi-\u003ehttp.rx_content_length;\n\n\t/*\n\t * At this point we have transitioned from deferred\n\t * action to expecting BODY on the stream wsi, if it's\n\t * in a bundle like h2. So if the stream wsi has its\n\t * own buflist, we need to deal with that first.\n\t */\n\n\twhile (1) {\n\t\tstruct lws_tokens ebuf;\n\t\tint m;\n\n\t\tebuf.len \u003d (int)lws_buflist_next_segment_len(\u0026wsi-\u003ebuflist,\n\t\t\t\t\t\t\t \u0026ebuf.token);\n\t\tif (!ebuf.len)\n\t\t\tbreak;\n\n\t\tlwsl_debug(\u0022%s: consuming %d\u005cn\u0022, __func__, (int)ebuf.len);\n\t\tm \u003d lws_read_h1(wsi, ebuf.token, (lws_filepos_t)ebuf.len);\n\t\tif (m \u003c 0)\n\t\t\treturn -1;\n\n\t\tif (lws_buflist_aware_finished_consuming(wsi, \u0026ebuf, m, 1,\n\t\t\t\t\t\t\t __func__))\n\t\t\treturn -1;\n\t}\n\n\treturn 0;\n\nbail_nuke_ah:\n\tlws_header_table_detach(wsi, 1);\n\n\treturn 1;\n}\n\nint\nlws_confirm_host_header(struct lws *wsi)\n{\n\tstruct lws_tokenize ts;\n\tlws_tokenize_elem e;\n\tint port \u003d 80, n;\n\tchar buf[128];\n\n\t/*\n\t * this vhost wants us to validate what the\n\t * client sent against our vhost name\n\t */\n\n\tif (!lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {\n\t\tlwsl_info(\u0022%s: missing host on upgrade\u005cn\u0022, __func__);\n\n\t\treturn 1;\n\t}\n\n#if defined(LWS_WITH_TLS)\n\tif (wsi-\u003etls.ssl)\n\t\tport \u003d 443;\n#endif\n\n\tn \u003d lws_hdr_copy(wsi, buf, sizeof(buf) - 1, WSI_TOKEN_HOST);\n\tif (n \u003c\u003d 0) {\n\t\tlwsl_info(\u0022%s: missing or oversize host header\u005cn\u0022, __func__);\n\t\treturn 1;\n\t}\n\tts.len \u003d (size_t)n;\n\tlws_tokenize_init(\u0026ts, buf, LWS_TOKENIZE_F_DOT_NONTERM /* server.com */|\n\t\t\t\t LWS_TOKENIZE_F_NO_FLOATS /* 1.server.com */|\n\t\t\t\t LWS_TOKENIZE_F_MINUS_NONTERM /* a-b.com */);\n\n\tif (lws_tokenize(\u0026ts) !\u003d LWS_TOKZE_TOKEN)\n\t\tgoto bad_format;\n\n\tif (strncmp(ts.token, wsi-\u003ea.vhost-\u003ename, ts.token_len)) {\n\t\tbuf[(size_t)(ts.token - buf) + ts.token_len] \u003d '\u005c0';\n\t\tlwsl_info(\u0022%s: '%s' in host hdr but vhost name %s\u005cn\u0022,\n\t\t\t __func__, ts.token, wsi-\u003ea.vhost-\u003ename);\n\t\treturn 1;\n\t}\n\n\te \u003d lws_tokenize(\u0026ts);\n\tif (e \u003d\u003d LWS_TOKZE_DELIMITER \u0026\u0026 ts.token[0] \u003d\u003d ':') {\n\t\tif (lws_tokenize(\u0026ts) !\u003d LWS_TOKZE_INTEGER)\n\t\t\tgoto bad_format;\n\t\telse\n\t\t\tport \u003d atoi(ts.token);\n\t} else\n\t\tif (e !\u003d LWS_TOKZE_ENDED)\n\t\t\tgoto bad_format;\n\n\tif (wsi-\u003ea.vhost-\u003elisten_port !\u003d port \u0026\u0026\n\t\twsi-\u003ea.vhost-\u003elisten_port !\u003d CONTEXT_PORT_NO_LISTEN_SERVER) {\n\t\tlwsl_info(\u0022%s: host port %d mismatches vhost port %d\u005cn\u0022,\n\t\t\t __func__, port, wsi-\u003ea.vhost-\u003elisten_port);\n\t\treturn 1;\n\t}\n\n\tlwsl_debug(\u0022%s: host header OK\u005cn\u0022, __func__);\n\n\treturn 0;\n\nbad_format:\n\tlwsl_info(\u0022%s: bad host header format\u005cn\u0022, __func__);\n\n\treturn 1;\n}\n\n#if defined(LWS_WITH_SERVER)\nint\nlws_http_to_fallback(struct lws *wsi, unsigned char *obuf, size_t olen)\n{\n\tconst struct lws_role_ops *role \u003d \u0026role_ops_raw_skt;\n\tconst struct lws_protocols *p1, *protocol \u003d\n\t\t\t \u0026wsi-\u003ea.vhost-\u003eprotocols[wsi-\u003ea.vhost-\u003eraw_protocol_index];\n\tchar ipbuf[64];\n\tint n;\n\n\tif (wsi-\u003ea.vhost-\u003elisten_accept_role \u0026\u0026\n\t lws_role_by_name(wsi-\u003ea.vhost-\u003elisten_accept_role))\n\t\trole \u003d lws_role_by_name(wsi-\u003ea.vhost-\u003elisten_accept_role);\n\n\tif (wsi-\u003ea.vhost-\u003elisten_accept_protocol) {\n\t\tp1 \u003d lws_vhost_name_to_protocol(wsi-\u003ea.vhost,\n\t\t\t wsi-\u003ea.vhost-\u003elisten_accept_protocol);\n\t\tif (p1)\n\t\t\tprotocol \u003d p1;\n\t}\n\n\tlws_bind_protocol(wsi, protocol, __func__);\n\n\tlws_role_transition(wsi, LWSIFR_SERVER, LRS_ESTABLISHED, role);\n\n\tlws_header_table_detach(wsi, 0);\n\tlws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);\n\n\tn \u003d LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED;\n\tif (wsi-\u003erole_ops-\u003eadoption_cb[1])\n\t\tn \u003d wsi-\u003erole_ops-\u003eadoption_cb[1];\n\n\tipbuf[0] \u003d '\u005c0';\n#if !defined(LWS_PLAT_OPTEE)\n\tlws_get_peer_simple(wsi, ipbuf, sizeof(ipbuf));\n#endif\n\n\tlwsl_notice(\u0022%s: vh %s, peer: %s, role %s, \u0022\n\t\t \u0022protocol %s, cb %d, ah %p\u005cn\u0022, __func__, wsi-\u003ea.vhost-\u003ename,\n\t\t ipbuf, role ? role-\u003ename : \u0022null\u0022, protocol-\u003ename, n,\n\t\t wsi-\u003ehttp.ah);\n\n\tif ((wsi-\u003ea.protocol-\u003ecallback)(wsi, (enum lws_callback_reasons)n, wsi-\u003euser_space, NULL, 0))\n\t\treturn 1;\n\n\tn \u003d LWS_CALLBACK_RAW_RX;\n\tif (wsi-\u003erole_ops-\u003erx_cb[lwsi_role_server(wsi)])\n\t\tn \u003d wsi-\u003erole_ops-\u003erx_cb[lwsi_role_server(wsi)];\n\tif (wsi-\u003ea.protocol-\u003ecallback(wsi, (enum lws_callback_reasons)n, wsi-\u003euser_space, obuf, olen))\n\t\treturn 1;\n\n\treturn 0;\n}\n\nint\nlws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)\n{\n\tstruct lws_context *context \u003d lws_get_context(wsi);\n\tstruct lws_context_per_thread *pt \u003d \u0026context-\u003ept[(int)wsi-\u003etsi];\n#if defined(LWS_WITH_HTTP2)\n\tstruct allocated_headers *ah;\n#endif\n\tunsigned char *obuf \u003d *buf;\n#if defined(LWS_WITH_HTTP2)\n\tchar tbuf[128], *p;\n#endif\n\tsize_t olen \u003d len;\n\tint n \u003d 0, m, i;\n\n\tif (len \u003e\u003d 10000000) {\n\t\tlwsl_err(\u0022%s: assert: len %ld\u005cn\u0022, __func__, (long)len);\n\t\tassert(0);\n\t}\n\n\tif (!wsi-\u003ehttp.ah) {\n\t\tlwsl_err(\u0022%s: assert: NULL ah\u005cn\u0022, __func__);\n\t\tassert(0);\n\t}\n\n\twhile (len) {\n\t\tif (!lwsi_role_server(wsi) || !lwsi_role_http(wsi)) {\n\t\t\tlwsl_err(\u0022%s: bad wsi role 0x%x\u005cn\u0022, __func__,\n\t\t\t\t\t(int)lwsi_role(wsi));\n\t\t\tgoto bail_nuke_ah;\n\t\t}\n\n\t\ti \u003d (int)len;\n\t\tm \u003d lws_parse(wsi, *buf, \u0026i);\n\t\tlwsl_info(\u0022%s: parsed count %d\u005cn\u0022, __func__, (int)len - i);\n\t\t(*buf) +\u003d (int)len - i;\n\t\tlen \u003d (unsigned int)i;\n\n\t\tif (m \u003d\u003d LPR_DO_FALLBACK) {\n\n\t\t\t/*\n\t\t\t * http parser went off the rails and\n\t\t\t * LWS_SERVER_OPTION_FALLBACK_TO_APPLY_LISTEN_\n\t\t\t * ACCEPT_CONFIG is set on this vhost.\n\t\t\t *\n\t\t\t * We are transitioning from http with an AH, to\n\t\t\t * a backup role (raw-skt, by default). Drop\n\t\t\t * the ah, bind to the role with mode as\n\t\t\t * ESTABLISHED.\n\t\t\t */\nraw_transition:\n\n\t\t\tif (lws_http_to_fallback(wsi, obuf, olen)) {\n\t\t\t\tlwsl_info(\u0022%s: fallback -\u003e close\u005cn\u0022, __func__);\n\t\t\t\tgoto bail_nuke_ah;\n\t\t\t}\n\n\t\t\t(*buf) \u003d obuf + olen;\n\n\t\t\treturn 0;\n\t\t}\n\t\tif (m) {\n\t\t\tlwsl_info(\u0022lws_parse failed\u005cn\u0022);\n\t\t\tgoto bail_nuke_ah;\n\t\t}\n\n\t\t/* coverity... */\n\t\tif (!wsi-\u003ehttp.ah)\n\t\t\tgoto bail_nuke_ah;\n\n\t\tif (wsi-\u003ehttp.ah-\u003eparser_state !\u003d WSI_PARSING_COMPLETE)\n\t\t\tcontinue;\n\n\t\tlwsl_parser(\u0022%s: lws_parse sees parsing complete\u005cn\u0022, __func__);\n\n\t\t/* select vhost */\n\n\t\tif (wsi-\u003ea.vhost-\u003elisten_port \u0026\u0026\n\t\t lws_hdr_total_length(wsi, WSI_TOKEN_HOST)) {\n\t\t\tstruct lws_vhost *vhost \u003d lws_select_vhost(\n\t\t\t\tcontext, wsi-\u003ea.vhost-\u003elisten_port,\n\t\t\t\tlws_hdr_simple_ptr(wsi, WSI_TOKEN_HOST));\n\n\t\t\tif (vhost)\n\t\t\t\tlws_vhost_bind_wsi(vhost, wsi);\n\t\t} else\n\t\t\tlwsl_info(\u0022no host\u005cn\u0022);\n\n\t\tif ((!lwsi_role_h2(wsi) || !lwsi_role_server(wsi)) \u0026\u0026\n\t\t (!wsi-\u003econn_stat_done))\n\t\t\twsi-\u003econn_stat_done \u003d 1;\n\n\t\t/* check for unwelcome guests */\n#if defined(LWS_WITH_HTTP_UNCOMMON_HEADERS)\n\t\tif (wsi-\u003ea.context-\u003ereject_service_keywords) {\n\t\t\tconst struct lws_protocol_vhost_options *rej \u003d\n\t\t\t\t\twsi-\u003ea.context-\u003ereject_service_keywords;\n\t\t\tchar ua[384], *msg \u003d NULL;\n\n\t\t\tif (lws_hdr_copy(wsi, ua, sizeof(ua) - 1,\n\t\t\t\t\t WSI_TOKEN_HTTP_USER_AGENT) \u003e 0) {\n#ifdef LWS_WITH_ACCESS_LOG\n\t\t\t\tchar *uri_ptr \u003d NULL;\n\t\t\t\tint meth, uri_len;\n#endif\n\t\t\t\tua[sizeof(ua) - 1] \u003d '\u005c0';\n\t\t\t\twhile (rej) {\n\t\t\t\t\tif (!strstr(ua, rej-\u003ename)) {\n\t\t\t\t\t\trej \u003d rej-\u003enext;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tmsg \u003d strchr(rej-\u003evalue, ' ');\n\t\t\t\t\tif (msg)\n\t\t\t\t\t\tmsg++;\n\t\t\t\t\tlws_return_http_status(wsi,\n\t\t\t\t\t\t(unsigned int)atoi(rej-\u003evalue), msg);\n#ifdef LWS_WITH_ACCESS_LOG\n\t\t\t\t\tmeth \u003d lws_http_get_uri_and_method(wsi,\n\t\t\t\t\t\t\t\u0026uri_ptr, \u0026uri_len);\n\t\t\t\t\tif (meth \u003e\u003d 0)\n\t\t\t\t\t\tlws_prepare_access_log_info(wsi,\n\t\t\t\t\t\t\turi_ptr, uri_len, meth);\n\n\t\t\t\t\t/* wsi close will do the log */\n#endif\n\t\t\t\t\t/*\n\t\t\t\t\t * We don't want anything from\n\t\t\t\t\t * this rejected guy. Follow\n\t\t\t\t\t * the close flow, not the\n\t\t\t\t\t * transaction complete flow.\n\t\t\t\t\t */\n\t\t\t\t\tgoto bail_nuke_ah;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n#endif\n\t\t/*\n\t\t * So he may have come to us requesting one or another kind\n\t\t * of upgrade from http... but we may want to redirect him at\n\t\t * http level. In that case, we need to check the redirect\n\t\t * situation even though he's not actually wanting http and\n\t\t * prioritize returning that if there is one.\n\t\t */\n\n\t\t{\n\t\t\tconst struct lws_http_mount *hit \u003d NULL;\n\t\t\tint uri_len \u003d 0, ha, n;\n\t\t\tchar *uri_ptr \u003d NULL;\n\n\t\t\tn \u003d lws_http_get_uri_and_method(wsi, \u0026uri_ptr, \u0026uri_len);\n\t\t\tif (n \u003e\u003d 0) {\n\t\t\t\thit \u003d lws_find_mount(wsi, uri_ptr, uri_len);\n\t\t\t\tif (hit) {\n\t\t\t\t\tn \u003d lws_http_redirect_hit(pt, wsi, hit, uri_ptr,\n\t\t\t\t\t\t\t\t uri_len, \u0026ha);\n\t\t\t\t\tif (ha)\n\t\t\t\t\t\treturn n;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\n\n\t\tif (lws_hdr_total_length(wsi, WSI_TOKEN_CONNECT)) {\n\t\t\tlwsl_info(\u0022Changing to RAW mode\u005cn\u0022);\n\t\t\tgoto raw_transition;\n\t\t}\n\n\t\tlwsi_set_state(wsi, LRS_PRE_WS_SERVING_ACCEPT);\n\t\tlws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);\n\n\t\tif (lws_hdr_total_length(wsi, WSI_TOKEN_UPGRADE)) {\n\n\t\t\tconst char *up \u003d lws_hdr_simple_ptr(wsi,\n\t\t\t\t\t\t\t WSI_TOKEN_UPGRADE);\n\n\t\t\tif (strcasecmp(up, \u0022websocket\u0022) \u0026\u0026\n\t\t\t strcasecmp(up, \u0022h2c\u0022)) {\n\t\t\t\tlwsl_info(\u0022Unknown upgrade '%s'\u005cn\u0022, up);\n\n\t\t\t\tif (lws_return_http_status(wsi,\n\t\t\t\t\t\tHTTP_STATUS_FORBIDDEN, NULL) ||\n\t\t\t\t lws_http_transaction_completed(wsi))\n\t\t\t\t\tgoto bail_nuke_ah;\n\t\t\t}\n\n\t\t\tn \u003d user_callback_handle_rxflow(wsi-\u003ea.protocol-\u003ecallback,\n\t\t\t\t\twsi, LWS_CALLBACK_HTTP_CONFIRM_UPGRADE,\n\t\t\t\t\twsi-\u003euser_space, (char *)up, 0);\n\n\t\t\t/* just hang up? */\n\n\t\t\tif (n \u003c 0)\n\t\t\t\tgoto bail_nuke_ah;\n\n\t\t\t/* callback returned headers already, do t_c? */\n\n\t\t\tif (n \u003e 0) {\n\t\t\t\tif (lws_http_transaction_completed(wsi))\n\t\t\t\t\tgoto bail_nuke_ah;\n\n\t\t\t\t/* continue on */\n\n\t\t\t\treturn 0;\n\t\t\t}\n\n\t\t\t/* callback said 0, it was allowed */\n\n\t\t\tif (wsi-\u003ea.vhost-\u003eoptions \u0026\n\t\t\t LWS_SERVER_OPTION_VHOST_UPG_STRICT_HOST_CHECK \u0026\u0026\n\t\t\t lws_confirm_host_header(wsi))\n\t\t\t\tgoto bail_nuke_ah;\n\n\t\t\tif (!strcasecmp(up, \u0022websocket\u0022)) {\n#if defined(LWS_ROLE_WS)\n\t\t\t\tlws_metrics_tag_wsi_add(wsi, \u0022upg\u0022, \u0022ws\u0022);\n\t\t\t\tlwsl_info(\u0022Upgrade to ws\u005cn\u0022);\n\t\t\t\tgoto upgrade_ws;\n#endif\n\t\t\t}\n#if defined(LWS_WITH_HTTP2)\n\t\t\tif (!strcasecmp(up, \u0022h2c\u0022)) {\n\t\t\t\tlws_metrics_tag_wsi_add(wsi, \u0022upg\u0022, \u0022h2c\u0022);\n\t\t\t\tlwsl_info(\u0022Upgrade to h2c\u005cn\u0022);\n\t\t\t\tgoto upgrade_h2c;\n\t\t\t}\n#endif\n\t\t}\n\n\t\t/* no upgrade ack... he remained as HTTP */\n\n\t\tlwsl_info(\u0022%s: %s: No upgrade\u005cn\u0022, __func__, lws_wsi_tag(wsi));\n\n\t\tlwsi_set_state(wsi, LRS_ESTABLISHED);\n#if defined(LWS_WITH_FILE_OPS)\n\t\twsi-\u003ehttp.fop_fd \u003d NULL;\n#endif\n\n#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n\t\tlws_http_compression_validate(wsi);\n#endif\n\n\t\tlwsl_debug(\u0022%s: %s: ah %p\u005cn\u0022, __func__, lws_wsi_tag(wsi),\n\t\t\t (void *)wsi-\u003ehttp.ah);\n\n\t\tn \u003d lws_http_action(wsi);\n\n\t\treturn n;\n\n#if defined(LWS_WITH_HTTP2)\nupgrade_h2c:\n\t\tif (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP2_SETTINGS)) {\n\t\t\tlwsl_info(\u0022missing http2_settings\u005cn\u0022);\n\t\t\tgoto bail_nuke_ah;\n\t\t}\n\n\t\tlwsl_info(\u0022h2c upgrade...\u005cn\u0022);\n\n\t\tp \u003d lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP2_SETTINGS);\n\t\t/* convert the peer's HTTP-Settings */\n\t\tn \u003d lws_b64_decode_string(p, tbuf, sizeof(tbuf));\n\t\tif (n \u003c 0) {\n\t\t\tlwsl_parser(\u0022HTTP2_SETTINGS too long\u005cn\u0022);\n\t\t\treturn 1;\n\t\t}\n\n\t\twsi-\u003eupgraded_to_http2 \u003d 1;\n\n\t\t/* adopt the header info */\n\n\t\tah \u003d wsi-\u003ehttp.ah;\n\t\tlws_role_transition(wsi, LWSIFR_SERVER, LRS_H2_AWAIT_PREFACE,\n\t\t\t\t \u0026role_ops_h2);\n\n\t\t/* http2 union member has http union struct at start */\n\t\twsi-\u003ehttp.ah \u003d ah;\n\n\t\tif (!wsi-\u003eh2.h2n) {\n\t\t\twsi-\u003eh2.h2n \u003d lws_zalloc(sizeof(*wsi-\u003eh2.h2n), \u0022h2n\u0022);\n\t\t\tif (!wsi-\u003eh2.h2n)\n\t\t\t\treturn 1;\n\t\t}\n\n\t\tlws_h2_init(wsi);\n\n\t\t/* HTTP2 union */\n\n\t\tlws_h2_settings(wsi, \u0026wsi-\u003eh2.h2n-\u003epeer_set, (uint8_t *)tbuf, n);\n\n\t\tif (lws_hpack_dynamic_size(wsi, (int)wsi-\u003eh2.h2n-\u003epeer_set.s[\n\t\t H2SET_HEADER_TABLE_SIZE]))\n\t\t\treturn 1;\n\n\t\tstrcpy(tbuf, \u0022HTTP/1.1 101 Switching Protocols\u005cx0d\u005cx0a\u0022\n\t\t\t \u0022Connection: Upgrade\u005cx0d\u005cx0a\u0022\n\t\t\t \u0022Upgrade: h2c\u005cx0d\u005cx0a\u005cx0d\u005cx0a\u0022);\n\t\tm \u003d (int)strlen(tbuf);\n\t\tn \u003d lws_issue_raw(wsi, (unsigned char *)tbuf, (unsigned int)m);\n\t\tif (n !\u003d m) {\n\t\t\tlwsl_debug(\u0022http2 switch: ERROR writing to socket\u005cn\u0022);\n\t\t\treturn 1;\n\t\t}\n\n\t\treturn 0;\n#endif\n#if defined(LWS_ROLE_WS)\nupgrade_ws:\n\t\tif (lws_process_ws_upgrade(wsi))\n\t\t\tgoto bail_nuke_ah;\n\n\t\treturn 0;\n#endif\n\t} /* while all chars are handled */\n\n\treturn 0;\n\nbail_nuke_ah:\n\t/* drop the header info */\n\tlws_header_table_detach(wsi, 1);\n\n\treturn 1;\n}\n#endif\n\nint LWS_WARN_UNUSED_RESULT\nlws_http_transaction_completed(struct lws *wsi)\n{\n\tint n;\n\n\tif (wsi-\u003ehttp.cgi_transaction_complete)\n\t\treturn 0;\n\n\tif (lws_has_buffered_out(wsi)\n#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n\t\t\t|| wsi-\u003ehttp.comp_ctx.buflist_comp ||\n\t wsi-\u003ehttp.comp_ctx.may_have_more\n#endif\n\t) {\n\t\t/*\n\t\t * ...so he tried to send something large as the http reply,\n\t\t * it went as a partial, but he immediately said the\n\t\t * transaction was completed.\n\t\t *\n\t\t * Defer the transaction completed until the last part of the\n\t\t * partial is sent.\n\t\t */\n\t\tlwsl_debug(\u0022%s: %s: deferring due to partial\u005cn\u0022, __func__,\n\t\t\t\tlws_wsi_tag(wsi));\n\t\twsi-\u003ehttp.deferred_transaction_completed \u003d 1;\n\t\tlws_callback_on_writable(wsi);\n\n\t\treturn 0;\n\t}\n\t/*\n\t * Are we finishing the transaction before we have consumed any body?\n\t *\n\t * For h1 this would kill keepalive pipelining, and for h2, considering\n\t * it can extend over multiple DATA frames, it would kill the network\n\t * connection.\n\t */\n\tif (wsi-\u003ehttp.rx_content_length \u0026\u0026 wsi-\u003ehttp.rx_content_remain) {\n\t\t/*\n\t\t * are we already in LRS_DISCARD_BODY and didn't clear the\n\t\t * remaining before trying to complete the transaction again?\n\t\t */\n\t\tif (lwsi_state(wsi) \u003d\u003d LRS_DISCARD_BODY)\n\t\t\treturn -1;\n\t\t/*\n\t\t * let's defer transaction completed processing until we\n\t\t * discarded the remaining body\n\t\t */\n\t\tlwsi_set_state(wsi, LRS_DISCARD_BODY);\n\n\t\treturn 0;\n\t}\n\n#if defined(LWS_WITH_SYS_METRICS)\n\t{\n\t\tchar tmp[10];\n\n\t\tlws_snprintf(tmp, sizeof(tmp), \u0022%u\u0022, wsi-\u003ehttp.response_code);\n\t\tlws_metrics_tag_wsi_add(wsi, \u0022status\u0022, tmp);\n\t}\n#endif\n\n\tlwsl_info(\u0022%s: %s\u005cn\u0022, __func__, lws_wsi_tag(wsi));\n\n#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n\tlws_http_compression_destroy(wsi);\n#endif\n\tlws_access_log(wsi);\n\n\tif (!wsi-\u003ehdr_parsing_completed\n#if defined(LWS_WITH_CGI)\n\t\t\t\u0026\u0026 !wsi-\u003ehttp.cgi\n#endif\n\t) {\n\t\tchar peer[64];\n\n#if !defined(LWS_PLAT_OPTEE)\n\t\tlws_get_peer_simple(wsi, peer, sizeof(peer) - 1);\n#else\n\t\tpeer[0] \u003d '\u005c0';\n#endif\n\t\tpeer[sizeof(peer) - 1] \u003d '\u005c0';\n\t\tlwsl_info(\u0022%s: (from %s) ignoring, ah parsing incomplete\u005cn\u0022,\n\t\t\t\t__func__, peer);\n\t\treturn 0;\n\t}\n\n#if defined(LWS_WITH_CGI)\n\tif (wsi-\u003ehttp.cgi) {\n\t\tlwsl_debug(\u0022%s: cleaning cgi\u005cn\u0022, __func__);\n\t\twsi-\u003ehttp.cgi_transaction_complete \u003d 1;\n\t\tlws_cgi_remove_and_kill(wsi);\n\t\tlws_spawn_piped_destroy(\u0026wsi-\u003ehttp.cgi-\u003elsp);\n\t\tlws_sul_cancel(\u0026wsi-\u003ehttp.cgi-\u003esul_grace);\n\n\t\tlws_free_set_NULL(wsi-\u003ehttp.cgi);\n\t\twsi-\u003ehttp.cgi_transaction_complete \u003d 0;\n\t}\n#endif\n\n\t/* if we can't go back to accept new headers, drop the connection */\n\tif (wsi-\u003emux_substream)\n\t\treturn 1;\n\n\tif (wsi-\u003eseen_zero_length_recv)\n\t\treturn 1;\n\n\tif (wsi-\u003ehttp.conn_type !\u003d HTTP_CONNECTION_KEEP_ALIVE) {\n\t\tlwsl_info(\u0022%s: %s: close connection\u005cn\u0022, __func__, lws_wsi_tag(wsi));\n\t\treturn 1;\n\t}\n\n\tif (lws_bind_protocol(wsi, \u0026wsi-\u003ea.vhost-\u003eprotocols[0], __func__))\n\t\treturn 1;\n\n\t/*\n\t * otherwise set ourselves up ready to go again, but because we have no\n\t * idea about the wsi writability, we make put it in a holding state\n\t * until we can verify POLLOUT. The part of this that confirms POLLOUT\n\t * with no partials is in lws_server_socket_service() below.\n\t */\n\tlwsl_debug(\u0022%s: %s: setting DEF_ACT from 0x%x: %p\u005cn\u0022, __func__,\n\t\t lws_wsi_tag(wsi), (int)wsi-\u003ewsistate, wsi-\u003ebuflist);\n\tlwsi_set_state(wsi, LRS_DEFERRING_ACTION);\n\twsi-\u003ehttp.tx_content_length \u003d 0;\n\twsi-\u003ehttp.tx_content_remain \u003d 0;\n\twsi-\u003ehdr_parsing_completed \u003d 0;\n\twsi-\u003esending_chunked \u003d 0;\n#ifdef LWS_WITH_ACCESS_LOG\n\twsi-\u003ehttp.access_log.sent \u003d 0;\n#endif\n#if defined(LWS_WITH_FILE_OPS) \u0026\u0026 (defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2))\n\tif (lwsi_role_http(wsi) \u0026\u0026 lwsi_role_server(wsi) \u0026\u0026\n\t wsi-\u003ehttp.fop_fd !\u003d NULL)\n\t\tlws_vfs_file_close(\u0026wsi-\u003ehttp.fop_fd);\n#endif\n\n\tn \u003d NO_PENDING_TIMEOUT;\n\tif (wsi-\u003ea.vhost-\u003ekeepalive_timeout)\n\t\tn \u003d PENDING_TIMEOUT_HTTP_KEEPALIVE_IDLE;\n\tlws_set_timeout(wsi, (enum pending_timeout)n, wsi-\u003ea.vhost-\u003ekeepalive_timeout);\n\n\t/*\n\t * We already know we are on http1.1 / keepalive and the next thing\n\t * coming will be another header set.\n\t *\n\t * If there is no pending rx and we still have the ah, drop it and\n\t * reacquire a new ah when the new headers start to arrive. (Otherwise\n\t * we needlessly hog an ah indefinitely.)\n\t *\n\t * However if there is pending rx and we know from the keepalive state\n\t * that is already at least the start of another header set, simply\n\t * reset the existing header table and keep it.\n\t */\n\tif (wsi-\u003ehttp.ah) {\n\t\t// lws_buflist_describe(\u0026wsi-\u003ebuflist, wsi, __func__);\n\t\tif (!lws_buflist_next_segment_len(\u0026wsi-\u003ebuflist, NULL)) {\n\t\t\tlwsl_debug(\u0022%s: %s: nothing in buflist, detaching ah\u005cn\u0022,\n\t\t\t\t __func__, lws_wsi_tag(wsi));\n\t\t\tlws_header_table_detach(wsi, 1);\n#ifdef LWS_WITH_TLS\n\t\t\t/*\n\t\t\t * additionally... if we are hogging an SSL instance\n\t\t\t * with no pending pipelined headers (or ah now), and\n\t\t\t * SSL is scarce, drop this connection without waiting\n\t\t\t */\n\n\t\t\tif (wsi-\u003ea.vhost-\u003etls.use_ssl \u0026\u0026\n\t\t\t wsi-\u003ea.context-\u003esimultaneous_ssl_restriction \u0026\u0026\n\t\t\t wsi-\u003ea.context-\u003esimultaneous_ssl \u003d\u003d\n\t\t\t\t wsi-\u003ea.context-\u003esimultaneous_ssl_restriction) {\n\t\t\t\tlwsl_info(\u0022%s: simultaneous_ssl_restriction\u005cn\u0022,\n\t\t\t\t\t __func__);\n\t\t\t\treturn 1;\n\t\t\t}\n#endif\n\t\t} else {\n\t\t\tlwsl_info(\u0022%s: %s: resetting/keeping ah as pipeline\u005cn\u0022,\n\t\t\t\t __func__, lws_wsi_tag(wsi));\n\t\t\tlws_header_table_reset(wsi, 0);\n\t\t\t/*\n\t\t\t * If we kept the ah, we should restrict the amount\n\t\t\t * of time we are willing to keep it. Otherwise it\n\t\t\t * will be bound the whole time the connection remains\n\t\t\t * open.\n\t\t\t */\n\t\t\tlws_set_timeout(wsi, PENDING_TIMEOUT_HOLDING_AH,\n\t\t\t\t\twsi-\u003ea.vhost-\u003ekeepalive_timeout);\n\t\t}\n\t\t/* If we're (re)starting on headers, need other implied init */\n\t\tif (wsi-\u003ehttp.ah)\n\t\t\twsi-\u003ehttp.ah-\u003eues \u003d URIES_IDLE;\n\n\t\t//lwsi_set_state(wsi, LRS_ESTABLISHED); // !!!\n\t} else\n\t\tif (lws_buflist_next_segment_len(\u0026wsi-\u003ebuflist, NULL))\n\t\t\tif (lws_header_table_attach(wsi, 0))\n\t\t\t\tlwsl_debug(\u0022acquired ah\u005cn\u0022);\n\n\tlwsl_debug(\u0022%s: %s: keep-alive await new transaction (state 0x%x)\u005cn\u0022,\n\t\t __func__, lws_wsi_tag(wsi), (int)wsi-\u003ewsistate);\n\tlws_callback_on_writable(wsi);\n\n\treturn 0;\n}\n\n#if defined(LWS_WITH_FILE_OPS)\nint\nlws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,\n\t\t const char *other_headers, int other_headers_len)\n{\n\tstruct lws_context *context \u003d lws_get_context(wsi);\n\tstruct lws_context_per_thread *pt \u003d \u0026context-\u003ept[(int)wsi-\u003etsi];\n\tunsigned char *response \u003d pt-\u003eserv_buf + LWS_PRE;\n#if defined(LWS_WITH_RANGES)\n\tstruct lws_range_parsing *rp \u003d \u0026wsi-\u003ehttp.range;\n#endif\n\tint ret \u003d 0, cclen \u003d 8, n \u003d HTTP_STATUS_OK;\n\tchar cache_control[50], *cc \u003d \u0022no-store\u0022;\n\tlws_fop_flags_t fflags \u003d LWS_O_RDONLY;\n\tconst struct lws_plat_file_ops *fops;\n\tlws_filepos_t total_content_length;\n\tunsigned char *p \u003d response;\n\tunsigned char *end \u003d p + context-\u003ept_serv_buf_size - LWS_PRE;\n\tconst char *vpath;\n#if defined(LWS_WITH_RANGES)\n\tint ranges;\n#endif\n\n\tif (wsi-\u003ehandling_404)\n\t\tn \u003d HTTP_STATUS_NOT_FOUND;\n\n\t/*\n\t * We either call the platform fops .open with first arg platform fops,\n\t * or we call fops_zip .open with first arg platform fops, and fops_zip\n\t * open will decide whether to switch to fops_zip or stay with fops_def.\n\t *\n\t * If wsi-\u003ehttp.fop_fd is already set, the caller already opened it\n\t */\n\tif (!wsi-\u003ehttp.fop_fd) {\n\t\tfops \u003d lws_vfs_select_fops(wsi-\u003ea.context-\u003efops, file, \u0026vpath);\n\t\tfflags |\u003d lws_vfs_prepare_flags(wsi);\n\t\twsi-\u003ehttp.fop_fd \u003d fops-\u003eLWS_FOP_OPEN(fops, wsi-\u003ea.context-\u003efops,\n\t\t\t\t\t\t\tfile, vpath, \u0026fflags);\n\t\tif (!wsi-\u003ehttp.fop_fd) {\n\t\t\tlwsl_info(\u0022%s: Unable to open: '%s': errno %d\u005cn\u0022,\n\t\t\t\t __func__, file, errno);\n\t\t\tif (lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND,\n\t\t\t\t\t\t NULL))\n\t\t\t\t\t\treturn -1;\n\t\t\treturn !wsi-\u003emux_substream;\n\t\t}\n\t}\n\n\t/*\n\t * Caution... wsi-\u003ehttp.fop_fd is live from here\n\t */\n\n\twsi-\u003ehttp.filelen \u003d lws_vfs_get_length(wsi-\u003ehttp.fop_fd);\n\ttotal_content_length \u003d wsi-\u003ehttp.filelen;\n\n#if defined(LWS_WITH_RANGES)\n\tranges \u003d lws_ranges_init(wsi, rp, wsi-\u003ehttp.filelen);\n\n\tlwsl_debug(\u0022Range count %d\u005cn\u0022, ranges);\n\t/*\n\t * no ranges -\u003e 200;\n\t * 1 range -\u003e 206 + Content-Type: normal; Content-Range;\n\t * more -\u003e 206 + Content-Type: multipart/byteranges\n\t * \t\tRepeat the true Content-Type in each multipart header\n\t * \t\talong with Content-Range\n\t */\n\tif (ranges \u003c 0) {\n\t\t/* it means he expressed a range in Range:, but it was illegal */\n\t\tlws_return_http_status(wsi,\n\t\t\t\tHTTP_STATUS_REQ_RANGE_NOT_SATISFIABLE, NULL);\n\t\tif (lws_http_transaction_completed(wsi))\n\t\t\tgoto bail; /* \u003c0 means just hang up */\n\n\t\tlws_vfs_file_close(\u0026wsi-\u003ehttp.fop_fd);\n\n\t\treturn 0; /* \u003d\u003d 0 means we did the transaction complete */\n\t}\n\tif (ranges)\n\t\tn \u003d HTTP_STATUS_PARTIAL_CONTENT;\n#endif\n\n\tif (lws_add_http_header_status(wsi, (unsigned int)n, \u0026p, end))\n\t\tgoto bail;\n\n\tif ((wsi-\u003ehttp.fop_fd-\u003eflags \u0026 (LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP |\n\t\t LWS_FOP_FLAG_COMPR_IS_GZIP)) \u003d\u003d\n\t (LWS_FOP_FLAG_COMPR_ACCEPTABLE_GZIP | LWS_FOP_FLAG_COMPR_IS_GZIP)) {\n\t\tif (lws_add_http_header_by_token(wsi,\n\t\t\tWSI_TOKEN_HTTP_CONTENT_ENCODING,\n\t\t\t(unsigned char *)\u0022gzip\u0022, 4, \u0026p, end))\n\t\t\tgoto bail;\n\t\tlwsl_info(\u0022file is being provided in gzip\u005cn\u0022);\n\t}\n#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n\telse {\n\t\t/*\n\t\t * if we know its very compressible, and we can use\n\t\t * compression, then use the most preferred compression\n\t\t * method that the client said he will accept\n\t\t */\n\n\t\tif (!wsi-\u003einterpreting \u0026\u0026 (\n\t\t !strncmp(content_type, \u0022text/\u0022, 5) ||\n\t\t !strcmp(content_type, \u0022application/javascript\u0022) ||\n\t\t !strcmp(content_type, \u0022image/svg+xml\u0022)))\n\t\t\tlws_http_compression_apply(wsi, NULL, \u0026p, end, 0);\n\t}\n#endif\n\n\tif (\n#if defined(LWS_WITH_RANGES)\n\t ranges \u003c 2 \u0026\u0026\n#endif\n\t content_type \u0026\u0026 content_type[0])\n\t\tif (lws_add_http_header_by_token(wsi,\n\t\t\t\t\t\t WSI_TOKEN_HTTP_CONTENT_TYPE,\n\t\t\t\t\t\t (unsigned char *)content_type,\n\t\t\t\t\t\t (int)strlen(content_type),\n\t\t\t\t\t\t \u0026p, end))\n\t\t\tgoto bail;\n\n#if defined(LWS_WITH_RANGES)\n\tif (ranges \u003e\u003d 2) { /* multipart byteranges */\n\t\tlws_strncpy(wsi-\u003ehttp.multipart_content_type, content_type,\n\t\t\tsizeof(wsi-\u003ehttp.multipart_content_type));\n\n\t\tif (lws_add_http_header_by_token(wsi,\n\t\t\t\t\t\t WSI_TOKEN_HTTP_CONTENT_TYPE,\n\t\t\t\t\t\t (unsigned char *)\n\t\t\t\t\t\t \u0022multipart/byteranges; \u0022\n\t\t\t\t\t\t \u0022boundary\u003d_lws\u0022,\n\t\t\t \t \t \t 20, \u0026p, end))\n\t\t\tgoto bail;\n\n\t\t/*\n\t\t * our overall content length has to include\n\t\t *\n\t\t * - (n + 1) x \u0022_lws\u005cr\u005cn\u0022\n\t\t * - n x Content-Type: xxx/xxx\u005cr\u005cn\n\t\t * - n x Content-Range: bytes xxx-yyy/zzz\u005cr\u005cn\n\t\t * - n x /r/n\n\t\t * - the actual payloads (aggregated in rp-\u003eagg)\n\t\t *\n\t\t * Precompute it for the main response header\n\t\t */\n\n\t\ttotal_content_length \u003d (lws_filepos_t)rp-\u003eagg +\n\t\t\t\t 6 /* final _lws\u005cr\u005cn */;\n\n\t\tlws_ranges_reset(rp);\n\t\twhile (lws_ranges_next(rp)) {\n\t\t\tn \u003d lws_snprintf(cache_control, sizeof(cache_control),\n\t\t\t\t\t\u0022bytes %llu-%llu/%llu\u0022,\n\t\t\t\t\trp-\u003estart, rp-\u003eend, rp-\u003eextent);\n\n\t\t\ttotal_content_length \u003d total_content_length +\n\t\t\t\t\t(lws_filepos_t)(\n\t\t\t\t6 /* header _lws\u005cr\u005cn */ +\n\t\t\t\t/* Content-Type: xxx/xxx\u005cr\u005cn */\n\t\t\t\t14 + (int)strlen(content_type) + 2 +\n\t\t\t\t/* Content-Range: xxxx\u005cr\u005cn */\n\t\t\t\t15 + n + 2 +\n\t\t\t\t2); /* /r/n */\n\t\t}\n\n\t\tlws_ranges_reset(rp);\n\t\tlws_ranges_next(rp);\n\t}\n\n\tif (ranges \u003d\u003d 1) {\n\t\ttotal_content_length \u003d (lws_filepos_t)rp-\u003eagg;\n\t\tn \u003d lws_snprintf(cache_control, sizeof(cache_control),\n\t\t\t\t \u0022bytes %llu-%llu/%llu\u0022,\n\t\t\t\t rp-\u003estart, rp-\u003eend, rp-\u003eextent);\n\n\t\tif (lws_add_http_header_by_token(wsi,\n\t\t\t\t\t\t WSI_TOKEN_HTTP_CONTENT_RANGE,\n\t\t\t\t\t\t (unsigned char *)cache_control,\n\t\t\t\t\t\t n, \u0026p, end))\n\t\t\tgoto bail;\n\t}\n\n\twsi-\u003ehttp.range.inside \u003d 0;\n\n\tif (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_ACCEPT_RANGES,\n\t\t\t\t\t (unsigned char *)\u0022bytes\u0022, 5, \u0026p, end))\n\t\tgoto bail;\n#endif\n\n\tif (!wsi-\u003emux_substream) {\n\t\t/* for http/1.1 ... */\n\t\tif (!wsi-\u003esending_chunked\n#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n\t\t\t\t\u0026\u0026 !wsi-\u003ehttp.lcs\n#endif\n\t\t) {\n\t\t\t/* ... if not already using chunked and not using an\n\t\t\t * http compression translation, then send the naive\n\t\t\t * content length\n\t\t\t */\n\t\t\tif (lws_add_http_header_content_length(wsi,\n\t\t\t\t\t\ttotal_content_length, \u0026p, end))\n\t\t\t\tgoto bail;\n\t\t} else {\n\n#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n\t\t\tif (wsi-\u003ehttp.lcs) {\n\n\t\t\t\t/* ...otherwise, for http 1 it must go chunked.\n\t\t\t\t * For the compression case, the reason is we\n\t\t\t\t * compress on the fly and do not know the\n\t\t\t\t * compressed content-length until it has all\n\t\t\t\t * been sent. Http/1.1 pipelining must be able\n\t\t\t\t * to know where the transaction boundaries are\n\t\t\t\t * ... so chunking...\n\t\t\t\t */\n\t\t\t\tif (lws_add_http_header_by_token(wsi,\n\t\t\t\t\t\tWSI_TOKEN_HTTP_TRANSFER_ENCODING,\n\t\t\t\t\t\t(unsigned char *)\u0022chunked\u0022, 7,\n\t\t\t\t\t\t\u0026p, end))\n\t\t\t\t\tgoto bail;\n\n\t\t\t\t/*\n\t\t\t\t * ...this is fun, isn't it :-) For h1 that is\n\t\t\t\t * using an http compression translation, the\n\t\t\t\t * compressor must chunk its output privately.\n\t\t\t\t *\n\t\t\t\t * h2 doesn't need (or support) any of this\n\t\t\t\t * crap.\n\t\t\t\t */\n\t\t\t\tlwsl_debug(\u0022setting chunking\u005cn\u0022);\n\t\t\t\twsi-\u003ehttp.comp_ctx.chunking \u003d 1;\n\t\t\t}\n#endif\n\t\t}\n\t}\n\n\tif (wsi-\u003ecache_no) {\n\t\tcc \u003d cache_control;\n\t\tcclen \u003d sprintf(cache_control, \u0022no-cache\u0022);\n\t}\n\telse if (wsi-\u003ecache_secs \u0026\u0026 wsi-\u003ecache_reuse) {\n\t\tif (!wsi-\u003ecache_revalidate) {\n\t\t\tcc \u003d cache_control;\n\t\t\tcclen \u003d sprintf(cache_control, \u0022%s, max-age\u003d%u\u0022,\n intermediates[wsi-\u003ecache_intermediaries],\n wsi-\u003ecache_secs);\n\t\t} else {\n\t\t\tcc \u003d cache_control;\n\t\t\tcclen \u003d sprintf(cache_control,\n \u0022must-revalidate, %s, max-age\u003d%u\u0022,\n intermediates[wsi-\u003ecache_intermediaries],\n wsi-\u003ecache_secs);\n\t\t}\n\t}\n\n\t/* Only add cache control if its not specified by any other_headers. */\n\tif (!other_headers ||\n\t (!strstr(other_headers, \u0022cache-control\u0022) \u0026\u0026\n\t !strstr(other_headers, \u0022Cache-Control\u0022))) {\n\t\tif (lws_add_http_header_by_token(wsi,\n\t\t\t\tWSI_TOKEN_HTTP_CACHE_CONTROL,\n\t\t\t\t(unsigned char *)cc, cclen, \u0026p, end))\n\t\t\tgoto bail;\n\t}\n\n\tif (other_headers) {\n\t\tif ((end - p) \u003c other_headers_len)\n\t\t\tgoto bail;\n\t\tmemcpy(p, other_headers, (unsigned int)other_headers_len);\n\t\tp +\u003d other_headers_len;\n\t}\n\n\tif (lws_finalize_http_header(wsi, \u0026p, end))\n\t\tgoto bail;\n\n\tret \u003d lws_write(wsi, response, lws_ptr_diff_size_t(p, response), LWS_WRITE_HTTP_HEADERS);\n\tif (ret !\u003d (p - response)) {\n\t\tlwsl_err(\u0022_write returned %d from %ld\u005cn\u0022, ret,\n\t\t\t (long)(p - response));\n\t\tgoto bail;\n\t}\n\n\twsi-\u003ehttp.filepos \u003d 0;\n\tlwsi_set_state(wsi, LRS_ISSUING_FILE);\n\n\tif (lws_hdr_total_length(wsi, WSI_TOKEN_HEAD_URI)) {\n\t\t/* we do not emit the body */\n\t\tlws_vfs_file_close(\u0026wsi-\u003ehttp.fop_fd);\n\t\tif (lws_http_transaction_completed(wsi))\n\t\t\tgoto bail;\n\n\t\treturn 0;\n\t}\n\n\tlws_callback_on_writable(wsi);\n\n\treturn 0;\n\nbail:\n\tlws_vfs_file_close(\u0026wsi-\u003ehttp.fop_fd);\n\n\treturn -1;\n}\n#endif\n\n#if defined(LWS_WITH_FILE_OPS)\n\nint lws_serve_http_file_fragment(struct lws *wsi)\n{\n\tstruct lws_context *context \u003d wsi-\u003ea.context;\n\tstruct lws_context_per_thread *pt \u003d \u0026context-\u003ept[(int)wsi-\u003etsi];\n\tstruct lws_process_html_args args;\n\tlws_filepos_t amount, poss;\n\tunsigned char *p, *pstart;\n#if defined(LWS_WITH_RANGES)\n\tunsigned char finished \u003d 0;\n#endif\n#if defined(LWS_ROLE_H2)\n\tstruct lws *nwsi;\n#endif\n\tint n, m;\n\n\tlwsl_debug(\u0022wsi-\u003emux_substream %d\u005cn\u0022, wsi-\u003emux_substream);\n\n\tdo {\n\n\t\t/* priority 1: buffered output */\n\n\t\tif (lws_has_buffered_out(wsi)) {\n\t\t\tif (lws_issue_raw(wsi, NULL, 0) \u003c 0) {\n\t\t\t\tlwsl_info(\u0022%s: closing\u005cn\u0022, __func__);\n\t\t\t\tgoto file_had_it;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\t/* priority 2: buffered pre-compression-transform */\n\n#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n\tif (wsi-\u003ehttp.comp_ctx.buflist_comp ||\n\t wsi-\u003ehttp.comp_ctx.may_have_more) {\n\t\tenum lws_write_protocol wp \u003d LWS_WRITE_HTTP;\n\n\t\tlwsl_info(\u0022%s: completing comp partial (buflist %p, may %d)\u005cn\u0022,\n\t\t\t __func__, wsi-\u003ehttp.comp_ctx.buflist_comp,\n\t\t\t wsi-\u003ehttp.comp_ctx.may_have_more);\n\n\t\tif (lws_rops_fidx(wsi-\u003erole_ops, LWS_ROPS_write_role_protocol) \u0026\u0026\n\t\t lws_rops_func_fidx(wsi-\u003erole_ops, LWS_ROPS_write_role_protocol).\n\t\t\t\t\twrite_role_protocol(wsi, NULL, 0, \u0026wp) \u003c 0) {\n\t\t\tlwsl_info(\u0022%s signalling to close\u005cn\u0022, __func__);\n\t\t\tgoto file_had_it;\n\t\t}\n\t\tlws_callback_on_writable(wsi);\n\n\t\tbreak;\n\t}\n#endif\n\n\t\tif (wsi-\u003ehttp.filepos \u003d\u003d wsi-\u003ehttp.filelen)\n\t\t\tgoto all_sent;\n\n\t\tn \u003d 0;\n\t\tp \u003d pstart \u003d pt-\u003eserv_buf + LWS_H2_FRAME_HEADER_LENGTH;\n\n#if defined(LWS_WITH_RANGES)\n\t\tif (wsi-\u003ehttp.range.count_ranges \u0026\u0026 !wsi-\u003ehttp.range.inside) {\n\n\t\t\tlwsl_notice(\u0022%s: doing range start %llu\u005cn\u0022, __func__,\n\t\t\t\t wsi-\u003ehttp.range.start);\n\n\t\t\tif ((long long)lws_vfs_file_seek_cur(wsi-\u003ehttp.fop_fd,\n\t\t\t\t\t\t (lws_fileofs_t)wsi-\u003ehttp.range.start -\n\t\t\t\t\t\t (lws_fileofs_t)wsi-\u003ehttp.filepos) \u003c 0)\n\t\t\t\tgoto file_had_it;\n\n\t\t\twsi-\u003ehttp.filepos \u003d wsi-\u003ehttp.range.start;\n\n\t\t\tif (wsi-\u003ehttp.range.count_ranges \u003e 1) {\n\t\t\t\tn \u003d lws_snprintf((char *)p,\n\t\t\t\t\t\tcontext-\u003ept_serv_buf_size -\n\t\t\t\t\t\tLWS_H2_FRAME_HEADER_LENGTH,\n\t\t\t\t\t\u0022_lws\u005cx0d\u005cx0a\u0022\n\t\t\t\t\t\u0022Content-Type: %s\u005cx0d\u005cx0a\u0022\n\t\t\t\t\t\u0022Content-Range: bytes \u0022\n\t\t\t\t\t\t\u0022%llu-%llu/%llu\u005cx0d\u005cx0a\u0022\n\t\t\t\t\t\u0022\u005cx0d\u005cx0a\u0022,\n\t\t\t\t\twsi-\u003ehttp.multipart_content_type,\n\t\t\t\t\twsi-\u003ehttp.range.start,\n\t\t\t\t\twsi-\u003ehttp.range.end,\n\t\t\t\t\twsi-\u003ehttp.range.extent);\n\t\t\t\tp +\u003d n;\n\t\t\t}\n\n\t\t\twsi-\u003ehttp.range.budget \u003d wsi-\u003ehttp.range.end -\n\t\t\t\t\t\t wsi-\u003ehttp.range.start + 1;\n\t\t\twsi-\u003ehttp.range.inside \u003d 1;\n\t\t}\n#endif\n\n\t\tposs \u003d context-\u003ept_serv_buf_size;\n\n#if defined(LWS_ROLE_H2)\n\t\t/*\n\t\t * If it's h2, restrict any lump that we are sending to the\n\t\t * max h2 frame size the peer indicated he could handle in\n\t\t * his SETTINGS\n\t\t */\n\t\tnwsi \u003d lws_get_network_wsi(wsi);\n\t\tif (nwsi-\u003eh2.h2n \u0026\u0026\n\t\t poss \u003e (lws_filepos_t)nwsi-\u003eh2.h2n-\u003epeer_set.s[H2SET_MAX_FRAME_SIZE])\n\t\t\tposs \u003d (lws_filepos_t)nwsi-\u003eh2.h2n-\u003epeer_set.s[H2SET_MAX_FRAME_SIZE];\n#endif\n\t\tposs \u003d poss - (lws_filepos_t)(n + LWS_H2_FRAME_HEADER_LENGTH);\n\n\t\tif (wsi-\u003ehttp.tx_content_length)\n\t\t\tif (poss \u003e wsi-\u003ehttp.tx_content_remain)\n\t\t\t\tposs \u003d wsi-\u003ehttp.tx_content_remain;\n\n\t\t/*\n\t\t * If there is a hint about how much we will do well to send at\n\t\t * one time, restrict ourselves to only trying to send that.\n\t\t */\n\t\tif (wsi-\u003ea.protocol-\u003etx_packet_size \u0026\u0026\n\t\t poss \u003e wsi-\u003ea.protocol-\u003etx_packet_size)\n\t\t\tposs \u003d wsi-\u003ea.protocol-\u003etx_packet_size;\n\n\t\tif (lws_rops_fidx(wsi-\u003erole_ops, LWS_ROPS_tx_credit)) {\n\t\t\tlws_filepos_t txc \u003d (unsigned int)lws_rops_func_fidx(wsi-\u003erole_ops,\n\t\t\t\t\t\t\t LWS_ROPS_tx_credit).\n\t\t\t\t\ttx_credit(wsi, LWSTXCR_US_TO_PEER, 0);\n\n\t\t\tif (!txc) {\n\t\t\t\t/*\n\t\t\t\t * We shouldn't've been able to get the\n\t\t\t\t * WRITEABLE if we are skint\n\t\t\t\t */\n\t\t\t\tlwsl_notice(\u0022%s: %s: no tx credit\u005cn\u0022, __func__,\n\t\t\t\t\t\tlws_wsi_tag(wsi));\n\n\t\t\t\treturn 0;\n\t\t\t}\n\t\t\tif (txc \u003c poss)\n\t\t\t\tposs \u003d txc;\n\n\t\t\t/*\n\t\t\t * Tracking consumption of the actual payload amount\n\t\t\t * will be handled when the role data frame is sent...\n\t\t\t */\n\t\t}\n\n#if defined(LWS_WITH_RANGES)\n\t\tif (wsi-\u003ehttp.range.count_ranges) {\n\t\t\tif (wsi-\u003ehttp.range.count_ranges \u003e 1)\n\t\t\t\tposs -\u003d 7; /* allow for final boundary */\n\t\t\tif (poss \u003e wsi-\u003ehttp.range.budget)\n\t\t\t\tposs \u003d wsi-\u003ehttp.range.budget;\n\t\t}\n#endif\n\t\tif (wsi-\u003esending_chunked) {\n\t\t\t/* we need to drop the chunk size in here */\n\t\t\tp +\u003d 10;\n\t\t\t/* allow for the chunk to grow by 128 in translation */\n\t\t\tposs -\u003d 10 + 128;\n\t\t}\n\n\t\tamount \u003d 0;\n\t\tif (lws_vfs_file_read(wsi-\u003ehttp.fop_fd, \u0026amount, p, poss) \u003c 0)\n\t\t\tgoto file_had_it; /* caller will close */\n\n\t\tif (wsi-\u003esending_chunked)\n\t\t\tn \u003d (int)amount;\n\t\telse\n\t\t\tn \u003d lws_ptr_diff(p, pstart) + (int)amount;\n\n\t\tlwsl_debug(\u0022%s: sending %d\u005cn\u0022, __func__, n);\n\n\t\tif (n) {\n\t\t\tlws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT,\n\t\t\t\t\t(int)context-\u003etimeout_secs);\n\n\t\t\tif (wsi-\u003einterpreting) {\n\t\t\t\targs.p \u003d (char *)p;\n\t\t\t\targs.len \u003d n;\n\t\t\t\targs.max_len \u003d (int)(unsigned int)poss + 128;\n\t\t\t\targs.final \u003d wsi-\u003ehttp.filepos + (unsigned int)n \u003d\u003d\n\t\t\t\t\t\t\twsi-\u003ehttp.filelen;\n\t\t\t\targs.chunked \u003d wsi-\u003esending_chunked;\n\t\t\t\tif (user_callback_handle_rxflow(\n\t\t\t\t wsi-\u003ea.vhost-\u003eprotocols[\n\t\t\t\t (int)wsi-\u003eprotocol_interpret_idx].callback,\n\t\t\t\t wsi, LWS_CALLBACK_PROCESS_HTML,\n\t\t\t\t wsi-\u003euser_space, \u0026args, 0) \u003c 0)\n\t\t\t\t\tgoto file_had_it;\n\t\t\t\tn \u003d args.len;\n\t\t\t\tp \u003d (unsigned char *)args.p;\n\t\t\t} else\n\t\t\t\tp \u003d pstart;\n\n#if defined(LWS_WITH_RANGES)\n\t\t\tif (wsi-\u003ehttp.range.send_ctr + 1 \u003d\u003d\n\t\t\t\twsi-\u003ehttp.range.count_ranges \u0026\u0026 // last range\n\t\t\t wsi-\u003ehttp.range.count_ranges \u003e 1 \u0026\u0026 // was 2+ ranges (ie, multipart)\n\t\t\t wsi-\u003ehttp.range.budget - amount \u003d\u003d 0) {// final part\n\t\t\t\tn +\u003d lws_snprintf((char *)pstart + n, 6,\n\t\t\t\t\t\u0022_lws\u005cx0d\u005cx0a\u0022); // append trailing boundary\n\t\t\t\tlwsl_debug(\u0022added trailing boundary\u005cn\u0022);\n\t\t\t}\n#endif\n\t\t\tm \u003d lws_write(wsi, p, (unsigned int)n, wsi-\u003ehttp.filepos + amount \u003d\u003d\n\t\t\t\t\twsi-\u003ehttp.filelen ?\n\t\t\t\t\t LWS_WRITE_HTTP_FINAL : LWS_WRITE_HTTP);\n\t\t\tif (m \u003c 0)\n\t\t\t\tgoto file_had_it;\n\n\t\t\twsi-\u003ehttp.filepos +\u003d amount;\n\n#if defined(LWS_WITH_RANGES)\n\t\t\tif (wsi-\u003ehttp.range.count_ranges \u003e\u003d 1) {\n\t\t\t\twsi-\u003ehttp.range.budget -\u003d amount;\n\t\t\t\tif (wsi-\u003ehttp.range.budget \u003d\u003d 0) {\n\t\t\t\t\tlwsl_notice(\u0022range budget exhausted\u005cn\u0022);\n\t\t\t\t\twsi-\u003ehttp.range.inside \u003d 0;\n\t\t\t\t\twsi-\u003ehttp.range.send_ctr++;\n\n\t\t\t\t\tif (lws_ranges_next(\u0026wsi-\u003ehttp.range) \u003c 1) {\n\t\t\t\t\t\tfinished \u003d 1;\n\t\t\t\t\t\tgoto all_sent;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n#endif\n\n\t\t\tif (m !\u003d n) {\n\t\t\t\t/* adjust for what was not sent */\n\t\t\t\tif (lws_vfs_file_seek_cur(wsi-\u003ehttp.fop_fd,\n\t\t\t\t\t\t\t m - n) \u003d\u003d\n\t\t\t\t\t\t\t (lws_fileofs_t)-1)\n\t\t\t\t\tgoto file_had_it;\n\t\t\t}\n\t\t}\n\nall_sent:\n\t\tif ((!lws_has_buffered_out(wsi)\n#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n\t\t\t\t\u0026\u0026 !wsi-\u003ehttp.comp_ctx.buflist_comp \u0026\u0026\n\t\t !wsi-\u003ehttp.comp_ctx.may_have_more\n#endif\n\t\t ) \u0026\u0026 (wsi-\u003ehttp.filepos \u003e\u003d wsi-\u003ehttp.filelen\n#if defined(LWS_WITH_RANGES)\n\t\t || finished)\n#else\n\t\t)\n#endif\n\t\t) {\n\t\t\tlwsi_set_state(wsi, LRS_ESTABLISHED);\n\t\t\t/* we might be in keepalive, so close it off here */\n\t\t\tlws_vfs_file_close(\u0026wsi-\u003ehttp.fop_fd);\n\n\t\t\tlwsl_debug(\u0022file completed\u005cn\u0022);\n\n\t\t\tif (wsi-\u003ea.protocol-\u003ecallback \u0026\u0026\n\t\t\t user_callback_handle_rxflow(wsi-\u003ea.protocol-\u003ecallback,\n\t\t\t\t\twsi, LWS_CALLBACK_HTTP_FILE_COMPLETION,\n\t\t\t\t\twsi-\u003euser_space, NULL, 0) \u003c 0) {\n\t\t\t\t\t/*\n\t\t\t\t\t * For http/1.x, the choices from\n\t\t\t\t\t * transaction_completed are either\n\t\t\t\t\t * 0 to use the connection for pipelined\n\t\t\t\t\t * or nonzero to hang it up.\n\t\t\t\t\t *\n\t\t\t\t\t * However for http/2. while we are\n\t\t\t\t\t * still interested in hanging up the\n\t\t\t\t\t * nwsi if there was a network-level\n\t\t\t\t\t * fatal error, simply completing the\n\t\t\t\t\t * transaction is a matter of the stream\n\t\t\t\t\t * state, not the root connection at the\n\t\t\t\t\t * network level\n\t\t\t\t\t */\n\n\t\t\t\t\tif (wsi-\u003emux_substream)\n\t\t\t\t\t\treturn 1;\n\t\t\t\t\telse\n\t\t\t\t\t\treturn -1;\n\t\t\t\t}\n\n\t\t\tif (wsi-\u003ehttp.ah)\n\t\t\t\tlws_header_table_reset(wsi, 0);\n\n\n\t\t\treturn 1; /* \u003e0 indicates completed */\n\t\t}\n\t\t/*\n\t\t * while(1) here causes us to spam the whole file contents into\n\t\t * a hugely bloated output buffer if it ever can't send the\n\t\t * whole chunk...\n\t\t */\n\t} while (!lws_send_pipe_choked(wsi));\n\n\tlws_callback_on_writable(wsi);\n\n\treturn 0; /* indicates further processing must be done */\n\nfile_had_it:\n\tlws_vfs_file_close(\u0026wsi-\u003ehttp.fop_fd);\n\n\treturn -1;\n}\n\n#endif\n\n#if defined(LWS_WITH_SERVER)\nvoid\nlws_server_get_canonical_hostname(struct lws_context *context,\n\t\t\t\t const struct lws_context_creation_info *info)\n{\n\tif (lws_check_opt(info-\u003eoptions,\n\t\t\tLWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME))\n\t\treturn;\n#if !defined(LWS_PLAT_FREERTOS)\n\t/* find canonical hostname */\n\tif (gethostname((char *)context-\u003ecanonical_hostname,\n\t\t sizeof(context-\u003ecanonical_hostname) - 1))\n\t\tlws_strncpy((char *)context-\u003ecanonical_hostname, \u0022unknown\u0022,\n\t\t\t sizeof(context-\u003ecanonical_hostname));\n\n\tlwsl_cx_info(context, \u0022 canonical_hostname \u003d %s\u005cn\u0022,\n\t\t\t\t\tcontext-\u003ecanonical_hostname);\n#else\n\t(void)context;\n#endif\n}\n#endif\n\nint\nlws_chunked_html_process(struct lws_process_html_args *args,\n\t\t\t struct lws_process_html_state *s)\n{\n\tchar *sp, buffer[32];\n\tconst char *pc;\n\tint old_len, n;\n\n\t/* do replacements */\n\tsp \u003d args-\u003ep;\n\told_len \u003d args-\u003elen;\n\targs-\u003elen \u003d 0;\n\ts-\u003estart \u003d sp;\n\twhile (sp \u003c args-\u003ep + old_len) {\n\n\t\tif (args-\u003elen + 7 \u003e\u003d args-\u003emax_len) {\n\t\t\tlwsl_err(\u0022Used up interpret padding\u005cn\u0022);\n\t\t\treturn -1;\n\t\t}\n\n\t\tif ((!s-\u003epos \u0026\u0026 *sp \u003d\u003d '$') || s-\u003epos) {\n\t\t\tint hits \u003d 0, hit \u003d 0;\n\n\t\t\tif (!s-\u003epos)\n\t\t\t\ts-\u003estart \u003d sp;\n\t\t\ts-\u003eswallow[s-\u003epos++] \u003d *sp;\n\t\t\tif (s-\u003epos \u003d\u003d sizeof(s-\u003eswallow) - 1)\n\t\t\t\tgoto skip;\n\t\t\tfor (n \u003d 0; n \u003c s-\u003ecount_vars; n++)\n\t\t\t\tif (!strncmp(s-\u003eswallow, s-\u003evars[n], (unsigned int)s-\u003epos)) {\n\t\t\t\t\thits++;\n\t\t\t\t\thit \u003d n;\n\t\t\t\t}\n\t\t\tif (!hits) {\nskip:\n\t\t\t\ts-\u003eswallow[s-\u003epos] \u003d '\u005c0';\n\t\t\t\tmemcpy(s-\u003estart, s-\u003eswallow, (unsigned int)s-\u003epos);\n\t\t\t\targs-\u003elen++;\n\t\t\t\ts-\u003epos \u003d 0;\n\t\t\t\tsp \u003d s-\u003estart + 1;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (hits \u003d\u003d 1 \u0026\u0026 s-\u003epos \u003d\u003d (int)strlen(s-\u003evars[hit])) {\n\t\t\t\tpc \u003d s-\u003ereplace(s-\u003edata, hit);\n\t\t\t\tif (!pc)\n\t\t\t\t\tpc \u003d \u0022NULL\u0022;\n\t\t\t\tn \u003d (int)strlen(pc);\n\t\t\t\ts-\u003eswallow[s-\u003epos] \u003d '\u005c0';\n\t\t\t\tif (n !\u003d s-\u003epos) {\n\t\t\t\t\tmemmove(s-\u003estart + n, s-\u003estart + s-\u003epos,\n\t\t\t\t\t\t(unsigned int)(old_len - (sp - args-\u003ep) - 1));\n\t\t\t\t\told_len +\u003d (n - s-\u003epos) + 1;\n\t\t\t\t}\n\t\t\t\tmemcpy(s-\u003estart, pc, (unsigned int)n);\n\t\t\t\targs-\u003elen++;\n\t\t\t\tsp \u003d s-\u003estart + 1;\n\n\t\t\t\ts-\u003epos \u003d 0;\n\t\t\t}\n\t\t\tsp++;\n\t\t\tcontinue;\n\t\t}\n\n\t\targs-\u003elen++;\n\t\tsp++;\n\t}\n\n\tif (args-\u003echunked) {\n\t\t/* no space left for final chunk trailer */\n\t\tif (args-\u003efinal \u0026\u0026 args-\u003elen + 7 \u003e\u003d args-\u003emax_len)\n\t\t\treturn -1;\n\n\t\tn \u003d sprintf(buffer, \u0022%X\u005cx0d\u005cx0a\u0022, args-\u003elen);\n\n\t\targs-\u003ep -\u003d n;\n\t\tmemcpy(args-\u003ep, buffer, (unsigned int)n);\n\t\targs-\u003elen +\u003d n;\n\n\t\tif (args-\u003efinal) {\n\t\t\tsp \u003d args-\u003ep + args-\u003elen;\n\t\t\t*sp++ \u003d '\u005cx0d';\n\t\t\t*sp++ \u003d '\u005cx0a';\n\t\t\t*sp++ \u003d '0';\n\t\t\t*sp++ \u003d '\u005cx0d';\n\t\t\t*sp++ \u003d '\u005cx0a';\n\t\t\t*sp++ \u003d '\u005cx0d';\n\t\t\t*sp++ \u003d '\u005cx0a';\n\t\t\targs-\u003elen +\u003d 7;\n\t\t} else {\n\t\t\tsp \u003d args-\u003ep + args-\u003elen;\n\t\t\t*sp++ \u003d '\u005cx0d';\n\t\t\t*sp++ \u003d '\u005cx0a';\n\t\t\targs-\u003elen +\u003d 2;\n\t\t}\n\t}\n\n\treturn 0;\n}\n","s":{"c":1713583216,"u": 4495}} ],"g": 98196,"chitpc": 0,"ehitpc": 0,"indexed":0 , "ab": 0, "si": 0, "db":0, "di":0, "sat":0, "lfc": "7d0a"}