Project homepage Mailing List  Warmcat.com  API Docs  Github Mirror 
{"schema":"libjg2-1", "vpath":"/git/", "avatar":"/git/avatar/", "alang":"en-US,en;q\u003d0.5", "gen_ut":1638757721, "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":"982452722bd034690c98d64073aebadc", "oid":{ "oid": "e1a73c42096a9f94617a25440501d7adc4abbd9f", "alias": [ "refs/heads/main"]},"blobname": "lib/roles/h2/ops-h2.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 \u003cprivate-lib-core.h\u003e\n\n/*\n * These are the standardized defaults.\n * Override what actually goes in the vhost settings in platform or user code.\n * Leave these alone because they are used to determine \u0022what is different\n * from the protocol defaults\u0022.\n */\nconst struct http2_settings lws_h2_defaults \u003d { {\n\t1,\n\t/* H2SET_HEADER_TABLE_SIZE */\t\t\t4096,\n\t/* *** This controls how many entries in the dynamic table ***\n\t * Allows the sender to inform the remote endpoint of the maximum\n\t * size of the header compression table used to decode header\n\t * blocks, in octets. The encoder can select any size equal to or\n\t * less than this value by using signaling specific to the header\n\t * compression format inside a header block (see [COMPRESSION]).\n\t * The initial value is 4,096 octets.\n\t */\n\t/* H2SET_ENABLE_PUSH */\t\t\t\t 1,\n\t/* H2SET_MAX_CONCURRENT_STREAMS */\t 0x7fffffff,\n\t/* H2SET_INITIAL_WINDOW_SIZE */\t\t 65535,\n\t/* H2SET_MAX_FRAME_SIZE */\t\t 16384,\n\t/* H2SET_MAX_HEADER_LIST_SIZE */\t 0x7fffffff,\n\t/*\u003c This advisory setting informs a peer of the maximum size of\n\t * header list that the sender is prepared to accept, in octets.\n\t * The value is based on the uncompressed size of header fields,\n\t * including the length of the name and value in octets plus an\n\t * overhead of 32 octets for each header field.\n\t */\n\t/* H2SET_RESERVED7 */\t\t\t\t 0,\n\t/* H2SET_ENABLE_CONNECT_PROTOCOL */\t\t 0,\n}};\n\n/* these are the \u0022lws defaults\u0022... they can be overridden in plat */\n\nconst struct http2_settings lws_h2_stock_settings \u003d { {\n\t1,\n\t/* H2SET_HEADER_TABLE_SIZE */\t\t\t65536, /* ffox */\n\t/* *** This controls how many entries in the dynamic table ***\n\t * Allows the sender to inform the remote endpoint of the maximum\n\t * size of the header compression table used to decode header\n\t * blocks, in octets. The encoder can select any size equal to or\n\t * less than this value by using signaling specific to the header\n\t * compression format inside a header block (see [COMPRESSION]).\n\t * The initial value is 4,096 octets.\n\t *\n\t * Can't pass h2spec with less than 4096 here...\n\t */\n\t/* H2SET_ENABLE_PUSH */\t\t\t\t 0,\n\t/* H2SET_MAX_CONCURRENT_STREAMS */\t\t 24,\n\t/* H2SET_INITIAL_WINDOW_SIZE */\t\t 0,\n\t/*\u003c This is managed by explicit WINDOW_UPDATE. Because otherwise no\n\t * way to precisely control it when we do want to.\n\t */\n\t/* H2SET_MAX_FRAME_SIZE */\t\t 16384,\n\t/* H2SET_MAX_HEADER_LIST_SIZE */\t 4096,\n\t/*\u003c This advisory setting informs a peer of the maximum size of\n\t * header list that the sender is prepared to accept, in octets.\n\t * The value is based on the uncompressed size of header fields,\n\t * including the length of the name and value in octets plus an\n\t * overhead of 32 octets for each header field.\n\t */\n\t/* H2SET_RESERVED7 */\t\t\t\t 0,\n\t/* H2SET_ENABLE_CONNECT_PROTOCOL */\t\t 1,\n}};\n\n/*\n * The wsi at this level is normally the network wsi... we can get called on\n * another path via lws_service_do_ripe_rxflow() on mux children too tho...\n */\n\nstatic int\nrops_handle_POLLIN_h2(struct lws_context_per_thread *pt, struct lws *wsi,\n\t\t struct lws_pollfd *pollfd)\n{\n\tstruct lws_tokens ebuf;\n\tunsigned int pending \u003d 0;\n\tchar buffered \u003d 0;\n\tstruct lws *wsi1;\n\tint n, m;\n\n#ifdef LWS_WITH_CGI\n\tif (wsi-\u003ehttp.cgi \u0026\u0026 (pollfd-\u003erevents \u0026 LWS_POLLOUT)) {\n\t\tif (lws_handle_POLLOUT_event(wsi, pollfd))\n\t\t\treturn LWS_HPI_RET_PLEASE_CLOSE_ME;\n\n\t\treturn LWS_HPI_RET_HANDLED;\n\t}\n#endif\n\n\t lwsl_info(\u0022%s: %s wsistate 0x%x, events %d, revents %d, pollout %d\u005cn\u0022, __func__,\n\t\t wsi-\u003elc.gutag, (unsigned int)wsi-\u003ewsistate,\n\t\t pollfd-\u003eevents, pollfd-\u003erevents,\n\t\t pollfd-\u003erevents \u0026 LWS_POLLOUT);\n\n\t /* !!! */\n\t if (wsi-\u003ewsistate \u003d\u003d 0x10000013) {\n\t\t wsi-\u003ebugcatcher++;\n\t\t if (wsi-\u003ebugcatcher \u003d\u003d 250) {\n\t\t\t lwsl_err(\u0022%s: BUGCATCHER\u005cn\u0022, __func__);\n\t\t\t return LWS_HPI_RET_PLEASE_CLOSE_ME;\n\t\t }\n\t } else\n\t\t wsi-\u003ebugcatcher \u003d 0;\n\n\t/*\n\t * something went wrong with parsing the handshake, and\n\t * we ended up back in the event loop without completing it\n\t */\n\tif (lwsi_state(wsi) \u003d\u003d LRS_PRE_WS_SERVING_ACCEPT) {\n\t\twsi-\u003esocket_is_permanently_unusable \u003d 1;\n\t\treturn LWS_HPI_RET_PLEASE_CLOSE_ME;\n\t}\n\n\tif (lwsi_state(wsi) \u003d\u003d LRS_WAITING_CONNECT) {\n#if defined(LWS_WITH_CLIENT)\n\t\tif ((pollfd-\u003erevents \u0026 LWS_POLLOUT) \u0026\u0026\n\t\t lws_handle_POLLOUT_event(wsi, pollfd)) {\n\t\t\tlwsl_debug(\u0022POLLOUT event closed it\u005cn\u0022);\n\t\t\treturn LWS_HPI_RET_PLEASE_CLOSE_ME;\n\t\t}\n\n\t\tn \u003d lws_http_client_socket_service(wsi, pollfd);\n\t\tif (n)\n\t\t\treturn LWS_HPI_RET_WSI_ALREADY_DIED;\n#endif\n\t\treturn LWS_HPI_RET_HANDLED;\n\t}\n\n\t/* 1: something requested a callback when it was OK to write */\n\n\tif ((pollfd-\u003erevents \u0026 LWS_POLLOUT) \u0026\u0026\n\t lwsi_state_can_handle_POLLOUT(wsi) \u0026\u0026\n\t lws_handle_POLLOUT_event(wsi, pollfd)) {\n\t\tif (lwsi_state(wsi) \u003d\u003d LRS_RETURNED_CLOSE)\n\t\t\tlwsi_set_state(wsi, LRS_FLUSHING_BEFORE_CLOSE);\n\t\t/* the write failed... it's had it */\n\t\twsi-\u003esocket_is_permanently_unusable \u003d 1;\n\n\t\treturn LWS_HPI_RET_PLEASE_CLOSE_ME;\n\t}\n\n\tif (lwsi_state(wsi) \u003d\u003d LRS_RETURNED_CLOSE ||\n\t lwsi_state(wsi) \u003d\u003d LRS_WAITING_TO_SEND_CLOSE ||\n\t lwsi_state(wsi) \u003d\u003d LRS_AWAITING_CLOSE_ACK) {\n\t\t/*\n\t\t * we stopped caring about anything except control\n\t\t * packets. Force flow control off, defeat tx\n\t\t * draining.\n\t\t */\n\t\tlws_rx_flow_control(wsi, 1);\n#if defined(LWS_ROLE_WS) \u0026\u0026 !defined(LWS_WITHOUT_EXTENSIONS)\n\t\tif (wsi-\u003ews)\n\t\t\twsi-\u003ews-\u003etx_draining_ext \u003d 0;\n#endif\n\t}\n\n\tif (wsi-\u003emux_substream || wsi-\u003eupgraded_to_http2) {\n\t\twsi1 \u003d lws_get_network_wsi(wsi);\n\t\tif (wsi1 \u0026\u0026 lws_has_buffered_out(wsi1)) {\n\n\t\t\tlwsl_info(\u0022%s: has buffered out\u005cn\u0022, __func__);\n\t\t\t/*\n\t\t\t * We cannot deal with any kind of new RX\n\t\t\t * because we are dealing with a partial send\n\t\t\t * (new RX may trigger new http_action() that\n\t\t\t * expect to be able to send)\n\t\t\t */\n\t\t\treturn LWS_HPI_RET_HANDLED;\n\t\t}\n\t}\n\nread:\n\t/* 3: network wsi buflist needs to be drained */\n\n\t// lws_buflist_describe(\u0026wsi-\u003ebuflist, wsi, __func__);\n\n\tebuf.len \u003d (int)lws_buflist_next_segment_len(\u0026wsi-\u003ebuflist,\n\t\t\t\t\t\t\u0026ebuf.token);\n\tif (ebuf.len) {\n\t\tlwsl_info(\u0022draining buflist (len %d)\u005cn\u0022, ebuf.len);\n\t\tbuffered \u003d 1;\n\t\tgoto drain;\n\t} else {\n\n\t\tif (wsi-\u003emux_substream) {\n\t\t\tlwsl_warn(\u0022%s: uh... %s mux child with nothing to drain\u005cn\u0022, __func__, lws_wsi_tag(wsi));\n\t\t\t// assert(0);\n\t\t\tlws_dll2_remove(\u0026wsi-\u003edll_buflist);\n\t\t\treturn LWS_HPI_RET_HANDLED;\n\t\t}\n\t}\n\n\tif (!lws_ssl_pending(wsi) \u0026\u0026\n\t !(pollfd-\u003erevents \u0026 pollfd-\u003eevents \u0026 LWS_POLLIN))\n\t\treturn LWS_HPI_RET_HANDLED;\n\n\t/* We have something to read... */\n\n\tif (!(lwsi_role_client(wsi) \u0026\u0026\n\t (lwsi_state(wsi) !\u003d LRS_ESTABLISHED \u0026\u0026\n\t // lwsi_state(wsi) !\u003d LRS_H1C_ISSUE_HANDSHAKE2 \u0026\u0026\n\t lwsi_state(wsi) !\u003d LRS_H2_WAITING_TO_SEND_HEADERS))) {\n\n\t\tebuf.token \u003d pt-\u003eserv_buf;\n\t\tebuf.len \u003d lws_ssl_capable_read(wsi,\n\t\t\t\t\tebuf.token,\n\t\t\t\t\twsi-\u003ea.context-\u003ept_serv_buf_size);\n\t\tswitch (ebuf.len) {\n\t\tcase 0:\n\t\t\tlwsl_info(\u0022%s: zero length read\u005cn\u0022, __func__);\n\t\t\treturn LWS_HPI_RET_PLEASE_CLOSE_ME;\n\t\tcase LWS_SSL_CAPABLE_MORE_SERVICE:\n\t\t\tlwsl_info(\u0022SSL Capable more service\u005cn\u0022);\n\t\t\treturn LWS_HPI_RET_HANDLED;\n\t\tcase LWS_SSL_CAPABLE_ERROR:\n\t\t\tlwsl_info(\u0022%s: LWS_SSL_CAPABLE_ERROR\u005cn\u0022, __func__);\n\t\t\treturn LWS_HPI_RET_PLEASE_CLOSE_ME;\n\t\t}\n\n\t\t// lwsl_notice(\u0022%s: Actual RX %d\u005cn\u0022, __func__, ebuf.len);\n\t\t// if (ebuf.len \u003e 0)\n\t\t//\tlwsl_hexdump_notice(ebuf.token, ebuf.len);\n\t} else\n\t\tlwsl_info(\u0022%s: skipped read\u005cn\u0022, __func__);\n\n\tif (ebuf.len \u003c 0)\n\t\treturn LWS_HPI_RET_PLEASE_CLOSE_ME;\n\ndrain:\n#if defined(LWS_WITH_CLIENT)\n\tif (lwsi_role_http(wsi) \u0026\u0026 lwsi_role_client(wsi) \u0026\u0026\n\t wsi-\u003ehdr_parsing_completed \u0026\u0026 !wsi-\u003etold_user_closed) {\n\n\t\t/*\n\t\t * In SSL mode we get POLLIN notification about\n\t\t * encrypted data in.\n\t\t *\n\t\t * But that is not necessarily related to decrypted\n\t\t * data out becoming available; in may need to perform\n\t\t * other in or out before that happens.\n\t\t *\n\t\t * simply mark ourselves as having readable data\n\t\t * and turn off our POLLIN\n\t\t */\n\t\twsi-\u003eclient_rx_avail \u003d 1;\n\t\tif (lws_change_pollfd(wsi, LWS_POLLIN, 0))\n\t\t\treturn LWS_HPI_RET_PLEASE_CLOSE_ME;\n\n\t\t/* let user code know, he'll usually ask for writeable\n\t\t * callback and drain / re-enable it there\n\t\t */\n\t\tif (user_callback_handle_rxflow(\n\t\t\t\twsi-\u003ea.protocol-\u003ecallback,\n\t\t\t\twsi, LWS_CALLBACK_RECEIVE_CLIENT_HTTP,\n\t\t\t\twsi-\u003euser_space, NULL, 0)) {\n\t\t\tlwsl_info(\u0022RECEIVE_CLIENT_HTTP closed it\u005cn\u0022);\n\t\t\treturn LWS_HPI_RET_PLEASE_CLOSE_ME;\n\t\t}\n\n\t\treturn LWS_HPI_RET_HANDLED;\n\t}\n#endif\n\n\t/* service incoming data */\n\n\tif (ebuf.len) {\n\t\tn \u003d 0;\n\t\tif (lwsi_role_h2(wsi) \u0026\u0026 lwsi_state(wsi) !\u003d LRS_BODY \u0026\u0026\n\t\t lwsi_state(wsi) !\u003d LRS_DISCARD_BODY)\n\t\t\tn \u003d lws_read_h2(wsi, ebuf.token, (unsigned int)ebuf.len);\n\t\telse\n\t\t\tn \u003d lws_read_h1(wsi, ebuf.token, (unsigned int)ebuf.len);\n\n\t\tif (n \u003c 0) {\n\t\t\t/* we closed wsi */\n\t\t\treturn LWS_HPI_RET_WSI_ALREADY_DIED;\n\t\t}\n\n\t\tif (n \u0026\u0026 buffered) {\n\t\t\t// lwsl_notice(\u0022%s: h2 use %d\u005cn\u0022, __func__, n);\n\t\t\tm \u003d (int)lws_buflist_use_segment(\u0026wsi-\u003ebuflist, (size_t)n);\n\t\t\tlwsl_info(\u0022%s: draining rxflow: used %d, next %d\u005cn\u0022,\n\t\t\t\t __func__, n, m);\n\t\t\tif (!m) {\n\t\t\t\tlwsl_notice(\u0022%s: removed %s from dll_buflist\u005cn\u0022,\n\t\t\t\t\t __func__, lws_wsi_tag(wsi));\n\t\t\t\tlws_dll2_remove(\u0026wsi-\u003edll_buflist);\n\t\t\t}\n\t\t} else\n\t\t\tif (n \u0026\u0026 n \u003c ebuf.len \u0026\u0026 ebuf.len \u003e 0) {\n\t\t\t\t// lwsl_notice(\u0022%s: h2 append seg %d\u005cn\u0022, __func__, ebuf.len - n);\n\t\t\t\tm \u003d lws_buflist_append_segment(\u0026wsi-\u003ebuflist,\n\t\t\t\t\t\tebuf.token + n,\n\t\t\t\t\t\t(unsigned int)(ebuf.len - n));\n\t\t\t\tif (m \u003c 0)\n\t\t\t\t\treturn LWS_HPI_RET_PLEASE_CLOSE_ME;\n\t\t\t\tif (m) {\n\t\t\t\t\tlwsl_debug(\u0022%s: added %s to rxflow list\u005cn\u0022,\n\t\t\t\t\t\t __func__, lws_wsi_tag(wsi));\n\t\t\t\t\tif (lws_dll2_is_detached(\u0026wsi-\u003edll_buflist))\n\t\t\t\t\t\tlws_dll2_add_head(\u0026wsi-\u003edll_buflist,\n\t\t\t\t\t\t\t \u0026pt-\u003edll_buflist_owner);\n\t\t\t\t}\n\t\t\t}\n\t}\n\n\t// lws_buflist_describe(\u0026wsi-\u003ebuflist, wsi, __func__);\n\n#if 0\n\n\t/*\n\t * This seems to be too aggressive... we don't want the ah stuck\n\t * there but eg, WINDOW_UPDATE may come and detach it if we leave\n\t * it like that... it will get detached at stream close\n\t */\n\n\tif (wsi-\u003ehttp.ah\n#if defined(LWS_WITH_CLIENT)\n\t\t\t\u0026\u0026 !wsi-\u003eclient_h2_alpn\n#endif\n\t\t\t) {\n\t\tlwsl_err(\u0022xxx\u005cn\u0022);\n\n\t\tlws_header_table_detach(wsi, 0);\n\t}\n#endif\n\n\tpending \u003d (unsigned int)lws_ssl_pending(wsi);\n\tif (pending) {\n\t\t// lwsl_info(\u0022going around\u005cn\u0022);\n\t\tgoto read;\n\t}\n\n\treturn LWS_HPI_RET_HANDLED;\n}\n\nint rops_handle_POLLOUT_h2(struct lws *wsi)\n{\n\t// lwsl_notice(\u0022%s\u005cn\u0022, __func__);\n\n\tif (lwsi_state(wsi) \u003d\u003d LRS_ISSUE_HTTP_BODY)\n\t\treturn LWS_HP_RET_USER_SERVICE;\n\n\t/*\n\t * Priority 1: H2 protocol packets\n\t */\n\tif ((wsi-\u003eupgraded_to_http2\n#if defined(LWS_WITH_CLIENT)\n\t\t\t|| wsi-\u003eclient_h2_alpn\n#endif\n\t\t\t) \u0026\u0026 wsi-\u003eh2.h2n-\u003epps) {\n\t\tlwsl_info(\u0022servicing pps\u005cn\u0022);\n\t\t/*\n\t\t * this is called on the network connection, but may close\n\t\t * substreams... that may affect callers\n\t\t */\n\t\tif (lws_h2_do_pps_send(wsi)) {\n\t\t\twsi-\u003esocket_is_permanently_unusable \u003d 1;\n\t\t\treturn LWS_HP_RET_BAIL_DIE;\n\t\t}\n\t\tif (wsi-\u003eh2.h2n-\u003epps)\n\t\t\treturn LWS_HP_RET_BAIL_OK;\n\n\t\t/* we can resume whatever we were doing */\n\t\tlws_rx_flow_control(wsi, LWS_RXFLOW_REASON_APPLIES_ENABLE |\n\t\t\t\t\t LWS_RXFLOW_REASON_H2_PPS_PENDING);\n\n\t\treturn LWS_HP_RET_BAIL_OK; /* leave POLLOUT active */\n\t}\n\n\t/* Priority 2: if we are closing, not allowed to send more data frags\n\t *\t which means user callback or tx ext flush banned now\n\t */\n\tif (lwsi_state(wsi) \u003d\u003d LRS_RETURNED_CLOSE)\n\t\treturn LWS_HP_RET_USER_SERVICE;\n\n\treturn LWS_HP_RET_USER_SERVICE;\n}\n\nstatic int\nrops_write_role_protocol_h2(struct lws *wsi, unsigned char *buf, size_t len,\n\t\t\t enum lws_write_protocol *wp)\n{\n\tunsigned char flags \u003d 0, base \u003d (*wp) \u0026 0x1f;\n\tsize_t olen \u003d len;\n\tint n;\n#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n\tunsigned char mtubuf[4096 + LWS_PRE];\n#endif\n\n\t/* if not in a state to send stuff, then just send nothing */\n\n\tif (!lwsi_role_ws(wsi) \u0026\u0026 !wsi-\u003emux_stream_immortal \u0026\u0026\n\t base !\u003d LWS_WRITE_HTTP \u0026\u0026\n\t base !\u003d LWS_WRITE_HTTP_FINAL \u0026\u0026\n\t base !\u003d LWS_WRITE_HTTP_HEADERS_CONTINUATION \u0026\u0026\n\t base !\u003d LWS_WRITE_HTTP_HEADERS \u0026\u0026 lwsi_state(wsi) !\u003d LRS_BODY \u0026\u0026\n\t ((lwsi_state(wsi) !\u003d LRS_RETURNED_CLOSE \u0026\u0026\n\t lwsi_state(wsi) !\u003d LRS_WAITING_TO_SEND_CLOSE \u0026\u0026\n\t lwsi_state(wsi) !\u003d LRS_ESTABLISHED \u0026\u0026\n\t lwsi_state(wsi) !\u003d LRS_AWAITING_CLOSE_ACK)\n#if defined(LWS_ROLE_WS)\n\t || base !\u003d LWS_WRITE_CLOSE\n#endif\n\t)) {\n\t\t//assert(0);\n\t\tlwsl_notice(\u0022%s: binning wsistate 0x%x %d: %s\u005cn\u0022, __func__,\n\t\t\t\t(unsigned int)wsi-\u003ewsistate, *wp, wsi-\u003ea.protocol ?\n\t\t\t\t\twsi-\u003ea.protocol-\u003ename : \u0022no protocol\u0022);\n\n\t\treturn 0;\n\t}\n\n\t/* compression transform... */\n\n#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n\tif (wsi-\u003ehttp.lcs) {\n\t\tunsigned char *out \u003d mtubuf + LWS_PRE;\n\t\tsize_t o \u003d sizeof(mtubuf) - LWS_PRE;\n\n\t\tn \u003d lws_http_compression_transform(wsi, buf, len, wp, \u0026out, \u0026o);\n\t\tif (n)\n\t\t\treturn n;\n\n\t\tlwsl_info(\u0022%s: %s: transformed %d bytes to %d \u0022\n\t\t\t \u0022(wp 0x%x, more %d)\u005cn\u0022, __func__,\n\t\t\t lws_wsi_tag(wsi), (int)len, (int)o, (int)*wp,\n\t\t\t wsi-\u003ehttp.comp_ctx.may_have_more);\n\n\t\tbuf \u003d out;\n\t\tlen \u003d o;\n\t\tbase \u003d (*wp) \u0026 0x1f;\n\n\t\tif (!len)\n\t\t\treturn (int)olen;\n\t}\n#endif\n\n\t/*\n\t * ws-over-h2 also ends up here after the ws framing applied\n\t */\n\n\tn \u003d LWS_H2_FRAME_TYPE_DATA;\n\tif (base \u003d\u003d LWS_WRITE_HTTP_HEADERS) {\n\t\tn \u003d LWS_H2_FRAME_TYPE_HEADERS;\n\t\tif (!((*wp) \u0026 LWS_WRITE_NO_FIN))\n\t\t\tflags \u003d LWS_H2_FLAG_END_HEADERS;\n\t\tif (wsi-\u003eh2.send_END_STREAM ||\n\t\t ((*wp) \u0026 LWS_WRITE_H2_STREAM_END)) {\n\t\t\tflags |\u003d LWS_H2_FLAG_END_STREAM;\n\t\t\twsi-\u003eh2.send_END_STREAM \u003d 1;\n\t\t}\n\t}\n\n\tif (base \u003d\u003d LWS_WRITE_HTTP_HEADERS_CONTINUATION) {\n\t\tn \u003d LWS_H2_FRAME_TYPE_CONTINUATION;\n\t\tif (!((*wp) \u0026 LWS_WRITE_NO_FIN))\n\t\t\tflags \u003d LWS_H2_FLAG_END_HEADERS;\n\t\tif (wsi-\u003eh2.send_END_STREAM ||\n\t\t ((*wp) \u0026 LWS_WRITE_H2_STREAM_END)) {\n\t\t\tflags |\u003d LWS_H2_FLAG_END_STREAM;\n\t\t\twsi-\u003eh2.send_END_STREAM \u003d 1;\n\t\t}\n\t}\n\n\tif ((base \u003d\u003d LWS_WRITE_HTTP ||\n\t base \u003d\u003d LWS_WRITE_HTTP_FINAL) \u0026\u0026\n\t wsi-\u003ehttp.tx_content_length) {\n\t\twsi-\u003ehttp.tx_content_remain -\u003d len;\n\t\tlwsl_info(\u0022%s: %s: tx_content_rem \u003d %llu\u005cn\u0022, __func__,\n\t\t\t lws_wsi_tag(wsi),\n\t\t\t (unsigned long long)wsi-\u003ehttp.tx_content_remain);\n\t\tif (!wsi-\u003ehttp.tx_content_remain) {\n\t\t\tlwsl_info(\u0022%s: selecting final write mode\u005cn\u0022, __func__);\n\t\t\tbase \u003d *wp \u003d LWS_WRITE_HTTP_FINAL;\n\t\t}\n\t}\n\n\tif (base \u003d\u003d LWS_WRITE_HTTP_FINAL || ((*wp) \u0026 LWS_WRITE_H2_STREAM_END)) {\n\t\tflags |\u003d LWS_H2_FLAG_END_STREAM;\n\t\tlwsl_info(\u0022%s: %s: setting END_STREAM, 0x%x\u005cn\u0022, __func__,\n\t\t\t\tlws_wsi_tag(wsi), flags);\n\t\twsi-\u003eh2.send_END_STREAM \u003d 1;\n\t}\n\n\tn \u003d lws_h2_frame_write(wsi, n, flags, wsi-\u003emux.my_sid, (unsigned int)len, buf);\n\tif (n \u003c 0)\n\t\treturn n;\n\n\t/* hide it may have been compressed... */\n\n\treturn (int)olen;\n}\n\n#if defined(LWS_WITH_SERVER)\nstatic int\nrops_check_upgrades_h2(struct lws *wsi)\n{\n#if defined(LWS_ROLE_WS)\n\tchar *p;\n\n\t/*\n\t * with H2 there's also a way to upgrade a stream to something\n\t * else... :method is CONNECT and :protocol says the name of\n\t * the new protocol we want to carry. We have to have sent a\n\t * SETTINGS saying that we support it though.\n\t */\n\tp \u003d lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD);\n\tif (!wsi-\u003ea.vhost-\u003eh2.set.s[H2SET_ENABLE_CONNECT_PROTOCOL] ||\n\t !wsi-\u003emux_substream || !p || strcmp(p, \u0022CONNECT\u0022))\n\t\treturn LWS_UPG_RET_CONTINUE;\n\n\tp \u003d lws_hdr_simple_ptr(wsi, WSI_TOKEN_COLON_PROTOCOL);\n\tif (!p || strcmp(p, \u0022websocket\u0022))\n\t\treturn LWS_UPG_RET_CONTINUE;\n\n\tlwsl_info(\u0022Upgrade h2 to ws\u005cn\u0022);\n\tlws_mux_mark_immortal(wsi);\n\twsi-\u003eh2_stream_carries_ws \u003d 1;\n\n\tlws_metrics_tag_wsi_add(wsi, \u0022upg\u0022, \u0022ws_over_h2\u0022);\n\n\tif (lws_process_ws_upgrade(wsi))\n\t\treturn LWS_UPG_RET_BAIL;\n\n\tlwsl_info(\u0022Upgraded h2 to ws OK\u005cn\u0022);\n\n\treturn LWS_UPG_RET_DONE;\n#else\n\treturn LWS_UPG_RET_CONTINUE;\n#endif\n}\n#endif\n\nstatic int\nrops_init_vhost_h2(struct lws_vhost *vh,\n\t\t const struct lws_context_creation_info *info)\n{\n\tvh-\u003eh2.set \u003d vh-\u003econtext-\u003eset;\n\tif (info-\u003ehttp2_settings[0]) {\n\t\tint n;\n\n\t\tfor (n \u003d 1; n \u003c LWS_H2_SETTINGS_LEN; n++)\n\t\t\tvh-\u003eh2.set.s[n] \u003d info-\u003ehttp2_settings[n];\n\t}\n\n\treturn 0;\n}\n\nint\nrops_pt_init_destroy_h2(struct lws_context *context,\n\t\t const struct lws_context_creation_info *info,\n\t\t struct lws_context_per_thread *pt, int destroy)\n{\n\t/* if not already set by plat, use lws default SETTINGS */\n\tif (!context-\u003eset.s[0])\n\t\tcontext-\u003eset \u003d lws_h2_stock_settings;\n\n\t/*\n\t * We only want to do this once... we will do it if we are built\n\t * otherwise h1 ops will do it (or nobody if no http at all)\n\t */\n#if !defined(LWS_ROLE_H2) \u0026\u0026 defined(LWS_WITH_SERVER)\n\tif (!destroy) {\n\n\t\tpt-\u003esul_ah_lifecheck.cb \u003d lws_sul_http_ah_lifecheck;\n\n\t\t__lws_sul_insert_us(\u0026pt-\u003ept_sul_owner[LWSSULLI_MISS_IF_SUSPENDED],\n\t\t\t\t \u0026pt-\u003esul_ah_lifecheck, 30 * LWS_US_PER_SEC);\n\t} else\n\t\tlws_dll2_remove(\u0026pt-\u003esul_ah_lifecheck.list);\n#endif\n\n\treturn 0;\n}\n\n\nstatic int\nrops_tx_credit_h2(struct lws *wsi, char peer_to_us, int add)\n{\n\tstruct lws *nwsi \u003d lws_get_network_wsi(wsi);\n\tint n;\n\n\tif (add) {\n\t\tif (peer_to_us \u003d\u003d LWSTXCR_PEER_TO_US) {\n\t\t\t/*\n\t\t\t * We want to tell the peer they can write an additional\n\t\t\t * \u0022add\u0022 bytes to us\n\t\t\t */\n\t\t\treturn lws_h2_update_peer_txcredit(wsi, (unsigned int)-1, add);\n\t\t}\n\n\t\t/*\n\t\t * We're being told we can write an additional \u0022add\u0022 bytes\n\t\t * to the peer\n\t\t */\n\n\t\twsi-\u003etxc.tx_cr +\u003d add;\n\t\tnwsi-\u003etxc.tx_cr +\u003d add;\n\n\t\treturn 0;\n\t}\n\n\tif (peer_to_us \u003d\u003d LWSTXCR_US_TO_PEER)\n\t\treturn lws_h2_tx_cr_get(wsi);\n\n\tn \u003d wsi-\u003etxc.peer_tx_cr_est;\n\tif (n \u003e nwsi-\u003etxc.peer_tx_cr_est)\n\t\tn \u003d nwsi-\u003etxc.peer_tx_cr_est;\n\n\treturn n;\n}\n\nstatic int\nrops_destroy_role_h2(struct lws *wsi)\n{\n\tstruct lws_context_per_thread *pt \u003d \u0026wsi-\u003ea.context-\u003ept[(int)wsi-\u003etsi];\n\tstruct allocated_headers *ah;\n\n\t/* we may not have an ah, but may be on the waiting list... */\n\tlwsl_info(\u0022%s: %s: ah det due to close\u005cn\u0022, __func__, lws_wsi_tag(wsi));\n\t__lws_header_table_detach(wsi, 0);\n\n\tah \u003d pt-\u003ehttp.ah_list;\n\n\twhile (ah) {\n\t\tif (ah-\u003ein_use \u0026\u0026 ah-\u003ewsi \u003d\u003d wsi) {\n\t\t\tlwsl_err(\u0022%s: ah leak: %s\u005cn\u0022, __func__, lws_wsi_tag(wsi));\n\t\t\tah-\u003ein_use \u003d 0;\n\t\t\tah-\u003ewsi \u003d NULL;\n\t\t\tpt-\u003ehttp.ah_count_in_use--;\n\t\t\tbreak;\n\t\t}\n\t\tah \u003d ah-\u003enext;\n\t}\n\n#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n\tlws_http_compression_destroy(wsi);\n#endif\n\n\tif (wsi-\u003eupgraded_to_http2 || wsi-\u003emux_substream) {\n\t\tlws_hpack_destroy_dynamic_header(wsi);\n\n\t\tif (wsi-\u003eh2.h2n)\n\t\t\tlws_free_set_NULL(wsi-\u003eh2.h2n);\n\t}\n\n\treturn 0;\n}\n\nstatic int\nrops_close_kill_connection_h2(struct lws *wsi, enum lws_close_status reason)\n{\n\n#if defined(LWS_WITH_HTTP_PROXY)\n\tif (wsi-\u003ehttp.proxy_clientside) {\n\n\t\twsi-\u003ehttp.proxy_clientside \u003d 0;\n\n\t\tif (user_callback_handle_rxflow(wsi-\u003ea.protocol-\u003ecallback,\n\t\t\t\t\t\twsi,\n\t\t\t\t\t LWS_CALLBACK_COMPLETED_CLIENT_HTTP,\n\t\t\t\t\t\twsi-\u003euser_space, NULL, 0))\n\t\t\twsi-\u003ehttp.proxy_clientside \u003d 0;\n\t}\n#endif\n\n\tif (wsi-\u003emux_substream \u0026\u0026 wsi-\u003eh2_stream_carries_ws)\n\t\tlws_h2_rst_stream(wsi, 0, \u0022none\u0022);\n/*\telse\n\t\tif (wsi-\u003emux_substream)\n\t\t\tlws_h2_rst_stream(wsi, H2_ERR_STREAM_CLOSED, \u0022swsi got closed\u0022);\n*/\n\n\tlwsl_info(\u0022 %s, his parent %s: siblings:\u005cn\u0022, lws_wsi_tag(wsi), lws_wsi_tag(wsi-\u003emux.parent_wsi));\n\tlws_wsi_mux_dump_children(wsi);\n\n\tif (wsi-\u003eupgraded_to_http2 || wsi-\u003emux_substream\n#if defined(LWS_WITH_CLIENT)\n\t\t\t|| wsi-\u003eclient_mux_substream\n#endif\n\t) {\n\t\tlwsl_info(\u0022closing %s: parent %s\u005cn\u0022, lws_wsi_tag(wsi),\n\t\t\t\tlws_wsi_tag(wsi-\u003emux.parent_wsi));\n\n\t\tif (wsi-\u003emux.child_list \u0026\u0026 lwsl_visible(LLL_INFO)) {\n\t\t\tlwsl_info(\u0022 parent %s: closing children: list:\u005cn\u0022, lws_wsi_tag(wsi));\n\t\t\tlws_wsi_mux_dump_children(wsi);\n\t\t}\n\t\tlws_wsi_mux_close_children(wsi, (int)reason);\n\t}\n\n\tif (wsi-\u003eupgraded_to_http2) {\n\t\t/* remove pps */\n\t\tstruct lws_h2_protocol_send *w \u003d wsi-\u003eh2.h2n-\u003epps, *w1;\n\n\t\twhile (w) {\n\t\t\tw1 \u003d w-\u003enext;\n\t\t\tfree(w);\n\t\t\tw \u003d w1;\n\t\t}\n\t\twsi-\u003eh2.h2n-\u003epps \u003d NULL;\n\t}\n\n\tif ((\n#if defined(LWS_WITH_CLIENT)\n\t\t\twsi-\u003eclient_mux_substream ||\n#endif\n\t\t\twsi-\u003emux_substream) \u0026\u0026\n\t wsi-\u003emux.parent_wsi) {\n\t\tlws_wsi_mux_sibling_disconnect(wsi);\n\t\tif (wsi-\u003eh2.pending_status_body)\n\t\t\tlws_free_set_NULL(wsi-\u003eh2.pending_status_body);\n\t}\n\n\treturn 0;\n}\n\nstatic int\nrops_callback_on_writable_h2(struct lws *wsi)\n{\n#if defined(LWS_WITH_CLIENT)\n\tstruct lws *network_wsi;\n#endif\n\tint already;\n\n//\tif (!lwsi_role_h2(wsi) \u0026\u0026 !lwsi_role_h2_ENCAPSULATION(wsi))\n//\t\treturn 0;\n\n\tif (wsi-\u003emux.requested_POLLOUT\n#if defined(LWS_WITH_CLIENT)\n\t\t\t\u0026\u0026 !wsi-\u003eclient_h2_alpn\n#endif\n\t) {\n\t\tlwsl_debug(\u0022already pending writable\u005cn\u0022);\n\t\t// return 1;\n\t}\n\n\t/* is this for DATA or for control messages? */\n\n\tif (wsi-\u003eupgraded_to_http2 \u0026\u0026 !wsi-\u003eh2.h2n-\u003epps \u0026\u0026\n\t lws_wsi_txc_check_skint(\u0026wsi-\u003etxc, lws_h2_tx_cr_get(wsi))) {\n\t\t/*\n\t\t * refuse his efforts to get WRITABLE if we have no credit and\n\t\t * no non-DATA pps to send\n\t\t */\n\t\tlwsl_err(\u0022%s: skint\u005cn\u0022, __func__);\n\t\treturn 0;\n\t}\n\n#if defined(LWS_WITH_CLIENT)\n\tnetwork_wsi \u003d lws_get_network_wsi(wsi);\n#endif\n\talready \u003d lws_wsi_mux_mark_parents_needing_writeable(wsi);\n\n\t/* for network action, act only on the network wsi */\n\n\tif (already\n#if defined(LWS_WITH_CLIENT)\n\t\t\t\u0026\u0026 !network_wsi-\u003eclient_h2_alpn\n\t\t\t\u0026\u0026 !network_wsi-\u003eclient_mux_substream\n#endif\n\t\t\t)\n\t\treturn 1;\n\n\treturn 0;\n}\n\n#if defined(LWS_WITH_SERVER)\nstatic int\nlws_h2_bind_for_post_before_action(struct lws *wsi)\n{\n\tconst struct lws_http_mount *hit;\n\tchar *uri_ptr \u003d NULL;\n\tuint8_t *buffered;\n\tint uri_len \u003d 0;\n\tconst char *p;\n\tsize_t blen;\n\n\tp \u003d lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_METHOD);\n\tif (!p || strcmp(p, \u0022POST\u0022))\n\t\treturn 0;\n\n\n\tif (!lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_PATH) ||\n\t !lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_PATH))\n\t\t/*\n\t\t * There must be a path. Actually this is checked at\n\t\t * http2.c along with the other required header\n\t\t * presence before we can get here.\n\t\t *\n\t\t * But Coverity insists to see us check it.\n\t\t */\n\t\treturn 1;\n\n\thit \u003d lws_find_mount(wsi,\n\t\t lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_PATH),\n\t\t lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COLON_PATH));\n\n\tlwsl_debug(\u0022%s: %s: hit %p: %s\u005cn\u0022, __func__,\n\t\t lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_PATH),\n\t\t hit, hit ? hit-\u003eorigin : \u0022null\u0022);\n\tif (hit) {\n\t\tconst struct lws_protocols *pp;\n\t\tconst char *name \u003d hit-\u003eorigin;\n\n\t\tif (hit-\u003eorigin_protocol \u003d\u003d LWSMPRO_CGI ||\n\t\t hit-\u003eorigin_protocol \u003d\u003d LWSMPRO_HTTP ||\n\t\t hit-\u003eorigin_protocol \u003d\u003d LWSMPRO_HTTPS)\n\t\t\treturn 0;\n\n\t\tif (hit-\u003eprotocol)\n\t\t\tname \u003d hit-\u003eprotocol;\n\t\telse\n\t\t\tif (hit-\u003eorigin_protocol \u003d\u003d LWSMPRO_FILE)\n\t\t\t\treturn 0;\n\n\t\tpp \u003d lws_vhost_name_to_protocol(wsi-\u003ea.vhost, name);\n\t\tif (!pp) {\n\t\t\tlwsl_info(\u0022Unable to find protocol '%s'\u005cn\u0022, name);\n\t\t\treturn 1;\n\t\t}\n\n\t\tif (lws_bind_protocol(wsi, pp, __func__))\n\t\t\treturn 1;\n\t}\n\tif (lws_http_get_uri_and_method(wsi, \u0026uri_ptr, \u0026uri_len) \u003e\u003d 0)\n\t\tif (wsi-\u003ea.protocol-\u003ecallback(wsi, LWS_CALLBACK_HTTP,\n\t\t\t\t\t wsi-\u003euser_space,\n\t\t\t\t\t hit ? uri_ptr +\n\t\t\t\t\t\t hit-\u003emountpoint_len : uri_ptr,\n\t\t\t\t\t (size_t)(hit ? uri_len -\n\t\t\t\t\t\t\t hit-\u003emountpoint_len :\n\t\t\t\t\t\t\t uri_len)))\n\t\t\treturn 1;\n\n\tlwsl_info(\u0022%s: setting LRS_BODY from 0x%x (%s)\u005cn\u0022, __func__,\n\t\t (int)wsi-\u003ewsistate, wsi-\u003ea.protocol-\u003ename);\n\n\tlwsi_set_state(wsi, LRS_BODY);\n\n\tif (wsi-\u003ehttp.content_length_explicitly_zero)\n\t\treturn 0;\n\n\t/*\n\t * Dump any stashed body\n\t */\n\n\twhile (((!wsi-\u003ehttp.content_length_given) ||\n\t\t wsi-\u003ehttp.rx_content_length) \u0026\u0026\n\t (blen \u003d lws_buflist_next_segment_len(\u0026wsi-\u003ebuflist, \u0026buffered))) {\n\n\t\tif ((size_t)wsi-\u003ehttp.rx_content_length \u003c blen)\n\t\t\tblen \u003d (size_t)wsi-\u003ehttp.rx_content_length;\n\n\t\tif (wsi-\u003ea.protocol-\u003ecallback(wsi, LWS_CALLBACK_HTTP_BODY,\n\t\t\t\twsi-\u003euser_space, buffered, blen))\n\t\t\treturn 1;\n\t\tlws_buflist_use_segment(\u0026wsi-\u003ebuflist, blen);\n\n\t\twsi-\u003ehttp.rx_content_length -\u003d blen;\n\t}\n\n\tif (!wsi-\u003ebuflist)\n\t\t/* Take us off the pt's \u0022wsi holding input buflist\u0022 list */\n\t\tlws_dll2_remove(\u0026wsi-\u003edll_buflist);\n\n\tif (wsi-\u003ehttp.content_length_given \u0026\u0026 wsi-\u003ehttp.rx_content_length)\n\t\t/* still a-ways to go */\n\t\treturn 0;\n\n\tif (!wsi-\u003ehttp.content_length_given \u0026\u0026 !wsi-\u003eh2.END_STREAM)\n\t\treturn 0;\n\n\tif (wsi-\u003ea.protocol-\u003ecallback(wsi, LWS_CALLBACK_HTTP_BODY_COMPLETION,\n\t\t\t\t wsi-\u003euser_space, NULL, 0))\n\t\treturn 1;\n\n\treturn 0;\n}\n#endif\n\n/*\n * we are the 'network wsi' for potentially many muxed child wsi with\n * no network connection of their own, who have to use us for all their\n * network actions. So we use a round-robin scheme to share out the\n * POLLOUT notifications to our children.\n *\n * But because any child could exhaust the socket's ability to take\n * writes, we can only let one child get notified each time.\n *\n * In addition children may be closed / deleted / added between POLLOUT\n * notifications, so we can't hold pointers\n */\n\nstatic int\nrops_perform_user_POLLOUT_h2(struct lws *wsi)\n{\n\tstruct lws **wsi2;\n#if defined(LWS_ROLE_WS)\n\tint write_type \u003d LWS_WRITE_PONG;\n#endif\n\tint n;\n\n\twsi \u003d lws_get_network_wsi(wsi);\n\n\twsi-\u003emux.requested_POLLOUT \u003d 0;\n//\tif (!wsi-\u003eh2.initialized) {\n//\t\tlwsl_info(\u0022pollout on uninitialized http2 conn\u005cn\u0022);\n//\t\treturn 0;\n//\t}\n\n\tlws_wsi_mux_dump_waiting_children(wsi);\n\n\twsi2 \u003d \u0026wsi-\u003emux.child_list;\n\tif (!*wsi2)\n\t\treturn 0;\n\n\tdo {\n\t\tstruct lws *w, **wa;\n\n\t\twa \u003d \u0026(*wsi2)-\u003emux.sibling_list;\n\t\tif (!(*wsi2)-\u003emux.requested_POLLOUT)\n\t\t\tgoto next_child;\n\n\t\t/*\n\t\t * we're going to do writable callback for this child.\n\t\t * move him to be the last child\n\t\t */\n\n\t\tlwsl_debug(\u0022servicing child %s\u005cn\u0022, lws_wsi_tag(*wsi2));\n\n\t\tw \u003d lws_wsi_mux_move_child_to_tail(wsi2);\n\n\t\tif (!w) {\n\t\t\twa \u003d \u0026wsi-\u003emux.child_list;\n\t\t\tgoto next_child;\n\t\t}\n\n\t\tlwsl_info(\u0022%s: child %s, sid %d, (wsistate 0x%x)\u005cn\u0022,\n\t\t\t __func__, lws_wsi_tag(w), w-\u003emux.my_sid,\n\t\t\t (unsigned int)w-\u003ewsistate);\n\n\t\t/* priority 1: post compression-transform buffered output */\n\n\t\tif (lws_has_buffered_out(w)) {\n\t\t\tlwsl_debug(\u0022%s: completing partial\u005cn\u0022, __func__);\n\t\t\tif (lws_issue_raw(w, NULL, 0) \u003c 0) {\n\t\t\t\tlwsl_info(\u0022%s signalling to close\u005cn\u0022, __func__);\n\t\t\t\tlws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS,\n\t\t\t\t\t\t \u0022h2 end stream 1\u0022);\n\t\t\t\twa \u003d \u0026wsi-\u003emux.child_list;\n\t\t\t\tgoto next_child;\n\t\t\t}\n\t\t\tlws_callback_on_writable(w);\n\t\t\twa \u003d \u0026wsi-\u003emux.child_list;\n\t\t\tgoto next_child;\n\t\t}\n\n\t\t/* priority 2: pre compression-transform buffered output */\n\n#if defined(LWS_WITH_HTTP_STREAM_COMPRESSION)\n\t\tif (w-\u003ehttp.comp_ctx.buflist_comp ||\n\t\t w-\u003ehttp.comp_ctx.may_have_more) {\n\t\t\tenum lws_write_protocol wp \u003d LWS_WRITE_HTTP;\n\n\t\t\tlwsl_info(\u0022%s: completing comp partial\u0022\n\t\t\t\t \u0022(buflist_comp %p, may %d)\u005cn\u0022,\n\t\t\t\t __func__, w-\u003ehttp.comp_ctx.buflist_comp,\n\t\t\t\t w-\u003ehttp.comp_ctx.may_have_more);\n\n\t\t\tif (rops_write_role_protocol_h2(w, NULL, 0, \u0026wp) \u003c 0) {\n\t\t\t\tlwsl_info(\u0022%s signalling to close\u005cn\u0022, __func__);\n\t\t\t\tlws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS,\n\t\t\t\t\t\t \u0022comp write fail\u0022);\n\t\t\t}\n\t\t\tlws_callback_on_writable(w);\n\t\t\twa \u003d \u0026wsi-\u003emux.child_list;\n\t\t\tgoto next_child;\n\t\t}\n#endif\n\n\t\t/* priority 3: if no buffered out and waiting for that... */\n\n\t\tif (lwsi_state(w) \u003d\u003d LRS_FLUSHING_BEFORE_CLOSE) {\n\t\t\tw-\u003esocket_is_permanently_unusable \u003d 1;\n\t\t\tlws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS,\n\t\t\t\t\t \u0022h2 end stream 1\u0022);\n\t\t\twa \u003d \u0026wsi-\u003emux.child_list;\n\t\t\tgoto next_child;\n\t\t}\n\n\t\t/* if we arrived here, even by looping, we checked choked */\n\t\tw-\u003ecould_have_pending \u003d 0;\n\t\twsi-\u003ecould_have_pending \u003d 0;\n\n\t\tif (w-\u003eh2.pending_status_body) {\n\t\t\tw-\u003eh2.send_END_STREAM \u003d 1;\n\t\t\tn \u003d lws_write(w, (uint8_t *)w-\u003eh2.pending_status_body +\n\t\t\t\t\t LWS_PRE,\n\t\t\t\t strlen(w-\u003eh2.pending_status_body +\n\t\t\t\t\t LWS_PRE), LWS_WRITE_HTTP_FINAL);\n\t\t\tlws_free_set_NULL(w-\u003eh2.pending_status_body);\n\t\t\tlws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS,\n\t\t\t\t\t \u0022h2 end stream 1\u0022);\n\t\t\twa \u003d \u0026wsi-\u003emux.child_list;\n\t\t\tgoto next_child;\n\t\t}\n\n#if defined(LWS_WITH_CLIENT)\n\t\tif (lwsi_state(w) \u003d\u003d LRS_H2_WAITING_TO_SEND_HEADERS) {\n\t\t\tif (lws_h2_client_handshake(w))\n\t\t\t\treturn -1;\n\n\t\t\tgoto next_child;\n\t\t}\n#endif\n\n#if defined(LWS_WITH_SERVER)\n\t\tif (lwsi_state(w) \u003d\u003d LRS_DEFERRING_ACTION) {\n\n\t\t\t/*\n\t\t\t * we had to defer the http_action to the POLLOUT\n\t\t\t * handler, because we know it will send something and\n\t\t\t * only in the POLLOUT handler do we know for sure\n\t\t\t * that there is no partial pending on the network wsi.\n\t\t\t */\n\n\t\t\tlwsi_set_state(w, LRS_ESTABLISHED);\n\n\t\t\tif (w-\u003ebuflist) {\n\t\t\t\tstruct lws_context_per_thread *pt;\n\n\t\t\t\tpt \u003d \u0026w-\u003ea.context-\u003ept[(int)w-\u003etsi];\n\t\t\t\tlwsl_debug(\u0022%s: added %s to rxflow list\u005cn\u0022,\n\t\t\t\t\t __func__, lws_wsi_tag(w));\n\t\t\t\tlws_dll2_add_head(\n\t\t\t\t\t\u0026w-\u003edll_buflist,\n\t\t\t\t\t\u0026pt-\u003edll_buflist_owner);\n\t\t\t}\n\n\t\t\tif (lws_h2_bind_for_post_before_action(w))\n\t\t\t\treturn -1;\n\n\t\t\t/*\n\t\t\t * Well, we could be getting a POST from the client, it\n\t\t\t * may not have any content-length. In that case, we\n\t\t\t * will be in LRS_BODY state, we can't actually start\n\t\t\t * the action until we had the body and the stream is\n\t\t\t * half-closed, indicating that we can reply\n\t\t\t */\n\n\t\t\tif (lwsi_state(w) \u003d\u003d LRS_BODY \u0026\u0026\n\t\t\t w-\u003eh2.h2_state !\u003d LWS_H2_STATE_HALF_CLOSED_REMOTE)\n\t\t\t\tgoto next_child;\n\n\t\t\tlwsl_info(\u0022 h2 action start...\u005cn\u0022);\n\t\t\tn \u003d lws_http_action(w);\n\t\t\tif (n \u003c 0)\n\t\t\t\tlwsl_info (\u0022 h2 action result %d\u005cn\u0022, n);\n\t\t\telse\n\t\t\tlwsl_info(\u0022 h2 action result %d \u0022\n\t\t\t\t \u0022(wsi-\u003ehttp.rx_content_remain %lld)\u005cn\u0022,\n\t\t\t\t n, w-\u003ehttp.rx_content_remain);\n\n\t\t\t/*\n\t\t\t * Commonly we only managed to start a larger transfer\n\t\t\t * that will complete asynchronously under its own wsi\n\t\t\t * states. In those cases we will hear about\n\t\t\t * END_STREAM going out in the POLLOUT handler.\n\t\t\t */\n\t\t\tif (n \u003e\u003d 0 \u0026\u0026 !w-\u003eh2.pending_status_body \u0026\u0026\n\t\t\t (n || w-\u003eh2.send_END_STREAM)) {\n\t\t\t\tlwsl_info(\u0022closing stream after h2 action\u005cn\u0022);\n\t\t\t\tlws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS,\n\t\t\t\t\t\t \u0022h2 end stream\u0022);\n\t\t\t\twa \u003d \u0026wsi-\u003emux.child_list;\n\t\t\t}\n\n\t\t\tif (n \u003c 0)\n\t\t\t\twa \u003d \u0026wsi-\u003emux.child_list;\n\n\t\t\tgoto next_child;\n\t\t}\n\n#if defined(LWS_WITH_FILE_OPS)\n\n\t\tif (lwsi_state(w) \u003d\u003d LRS_ISSUING_FILE) {\n\n\t\t\tif (lws_wsi_txc_check_skint(\u0026w-\u003etxc,\n\t\t\t\t\t\t lws_h2_tx_cr_get(w))) {\n\t\t\t\twa \u003d \u0026wsi-\u003emux.child_list;\n\t\t\t\tgoto next_child;\n\t\t\t}\n\n\t\t\t((volatile struct lws *)w)-\u003eleave_pollout_active \u003d 0;\n\n\t\t\t/* \u003e0 \u003d\u003d completion, \u003c0 \u003d\u003d error\n\t\t\t *\n\t\t\t * We'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION\n\t\t\t * callback when it's done. That's the case even if we\n\t\t\t * just completed the send, so wait for that.\n\t\t\t */\n\t\t\tn \u003d lws_serve_http_file_fragment(w);\n\t\t\tlwsl_debug(\u0022lws_serve_http_file_fragment says %d\u005cn\u0022, n);\n\n\t\t\t/*\n\t\t\t * We will often hear about out having sent the final\n\t\t\t * DATA here... if so close the actual wsi\n\t\t\t */\n\t\t\tif (n \u003c 0 || w-\u003eh2.send_END_STREAM) {\n\t\t\t\tlwsl_debug(\u0022Closing POLLOUT child %s\u005cn\u0022,\n\t\t\t\t\t\tlws_wsi_tag(w));\n\t\t\t\tlws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS,\n\t\t\t\t\t\t \u0022h2 end stream file\u0022);\n\t\t\t\twa \u003d \u0026wsi-\u003emux.child_list;\n\t\t\t\tgoto next_child;\n\t\t\t}\n\t\t\tif (n \u003e 0)\n\t\t\t\tif (lws_http_transaction_completed(w))\n\t\t\t\t\treturn -1;\n\t\t\tif (!n) {\n\t\t\t\tlws_callback_on_writable(w);\n\t\t\t\t(w)-\u003emux.requested_POLLOUT \u003d 1;\n\t\t\t}\n\n\t\t\tgoto next_child;\n\t\t}\n#endif\n#endif\n\n#if defined(LWS_ROLE_WS)\n\n\t\t/* Notify peer that we decided to close */\n\n\t\tif (lwsi_role_ws(w) \u0026\u0026\n\t\t lwsi_state(w) \u003d\u003d LRS_WAITING_TO_SEND_CLOSE) {\n\t\t\tlwsl_debug(\u0022sending close packet\u005cn\u0022);\n\t\t\tw-\u003ewaiting_to_send_close_frame \u003d 0;\n\t\t\tn \u003d lws_write(w, \u0026w-\u003ews-\u003eping_payload_buf[LWS_PRE],\n\t\t\t\t w-\u003ews-\u003eclose_in_ping_buffer_len,\n\t\t\t\t LWS_WRITE_CLOSE);\n\t\t\tif (n \u003e\u003d 0) {\n\t\t\t\tlwsi_set_state(w, LRS_AWAITING_CLOSE_ACK);\n\t\t\t\tlws_set_timeout(w, PENDING_TIMEOUT_CLOSE_ACK, 5);\n\t\t\t\tlwsl_debug(\u0022sent close frame, awaiting ack\u005cn\u0022);\n\t\t\t}\n\n\t\t\tgoto next_child;\n\t\t}\n\n\t\t/*\n\t\t * Acknowledge receipt of peer's notification he closed,\n\t\t * then logically close ourself\n\t\t */\n\n\t\tif ((lwsi_role_ws(w) \u0026\u0026 w-\u003ews-\u003epong_pending_flag) ||\n\t\t (lwsi_state(w) \u003d\u003d LRS_RETURNED_CLOSE \u0026\u0026\n\t\t w-\u003ews-\u003epayload_is_close)) {\n\n\t\t\tif (w-\u003ews-\u003epayload_is_close)\n\t\t\t\twrite_type \u003d LWS_WRITE_CLOSE |\n\t\t\t\t\t LWS_WRITE_H2_STREAM_END;\n\n\t\t\tn \u003d lws_write(w, \u0026w-\u003ews-\u003epong_payload_buf[LWS_PRE],\n\t\t\t\t w-\u003ews-\u003epong_payload_len, (enum lws_write_protocol)write_type);\n\t\t\tif (n \u003c 0)\n\t\t\t\treturn -1;\n\n\t\t\t/* well he is sent, mark him done */\n\t\t\tw-\u003ews-\u003epong_pending_flag \u003d 0;\n\t\t\tif (w-\u003ews-\u003epayload_is_close) {\n\t\t\t\t/* oh... a close frame... then we are done */\n\t\t\t\tlwsl_debug(\u0022Ack'd peer's close packet\u005cn\u0022);\n\t\t\t\tw-\u003ews-\u003epayload_is_close \u003d 0;\n\t\t\t\tlwsi_set_state(w, LRS_RETURNED_CLOSE);\n\t\t\t\tlws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS,\n\t\t\t\t\t\t \u0022returned close packet\u0022);\n\t\t\t\twa \u003d \u0026wsi-\u003emux.child_list;\n\t\t\t\tgoto next_child;\n\t\t\t}\n\n\t\t\tlws_callback_on_writable(w);\n\t\t\t(w)-\u003emux.requested_POLLOUT \u003d 1;\n\n\t\t\t/* otherwise for PING, leave POLLOUT active both ways */\n\t\t\tgoto next_child;\n\t\t}\n#endif\n\n\t\t/*\n\t\t * set client wsi to immortal long-poll mode; send END_STREAM\n\t\t * flag on headers to indicate to a server, that allows\n\t\t * it, that you want them to leave the stream in a long poll\n\t\t * ro immortal state. We have to send headers so the client\n\t\t * understands the http connection is ongoing.\n\t\t */\n\n\t\tif (w-\u003eh2.send_END_STREAM \u0026\u0026 w-\u003eh2.long_poll) {\n\t\t\tuint8_t buf[LWS_PRE + 1];\n\t\t\tenum lws_write_protocol wp \u003d 0;\n\n\t\t\tif (!rops_write_role_protocol_h2(w, buf + LWS_PRE, 0,\n\t\t\t\t\t\t\t \u0026wp)) {\n\t\t\t\tlwsl_info(\u0022%s: %s: entering ro long poll\u005cn\u0022,\n\t\t\t\t\t __func__, lws_wsi_tag(w));\n\t\t\t\tlws_mux_mark_immortal(w);\n\t\t\t} else\n\t\t\t\tlwsl_err(\u0022%s: %s: failed to set long poll\u005cn\u0022,\n\t\t\t\t\t\t__func__, lws_wsi_tag(w));\n\t\t\tgoto next_child;\n\t\t}\n\n\t\tif (lws_callback_as_writeable(w)) {\n\t\t\tlwsl_info(\u0022Closing POLLOUT child (end stream %d)\u005cn\u0022,\n\t\t\t\t w-\u003eh2.send_END_STREAM);\n\t\t\tlws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS,\n\t\t\t\t\t \u0022h2 pollout handle\u0022);\n\t\t\twa \u003d \u0026wsi-\u003emux.child_list;\n\t\t} else\n\t\t\t if (w-\u003eh2.send_END_STREAM)\n\t\t\t\tlws_h2_state(w, LWS_H2_STATE_HALF_CLOSED_LOCAL);\n\nnext_child:\n\t\twsi2 \u003d wa;\n\t} while (wsi2 \u0026\u0026 *wsi2 \u0026\u0026 !lws_send_pipe_choked(wsi));\n\n\t// lws_wsi_mux_dump_waiting_children(wsi);\n\n\tif (lws_wsi_mux_action_pending_writeable_reqs(wsi))\n\t\treturn -1;\n\n\treturn 0;\n}\n\nstatic struct lws *\nrops_encapsulation_parent_h2(struct lws *wsi)\n{\n\tif (wsi-\u003emux.parent_wsi)\n\t\treturn wsi-\u003emux.parent_wsi;\n\n\treturn NULL;\n}\n\nstatic int\nrops_alpn_negotiated_h2(struct lws *wsi, const char *alpn)\n{\n\tstruct allocated_headers *ah;\n\n\tlwsl_debug(\u0022%s: client %d\u005cn\u0022, __func__, lwsi_role_client(wsi));\n#if defined(LWS_WITH_CLIENT)\n\tif (lwsi_role_client(wsi)) {\n\t\tlwsl_info(\u0022%s: upgraded to H2\u005cn\u0022, __func__);\n\t\twsi-\u003eclient_h2_alpn \u003d 1;\n\t}\n#endif\n\n\twsi-\u003eupgraded_to_http2 \u003d 1;\n\n\t/* adopt the header info */\n\n\tah \u003d wsi-\u003ehttp.ah;\n\n\tlws_role_transition(wsi, lwsi_role_client(wsi) ? LWSIFR_CLIENT : LWSIFR_SERVER, LRS_H2_AWAIT_PREFACE,\n\t\t\t \u0026role_ops_h2);\n\n\t/* http2 union member has http union struct at start */\n\twsi-\u003ehttp.ah \u003d ah;\n\n\tif (!wsi-\u003eh2.h2n)\n\t\twsi-\u003eh2.h2n \u003d lws_zalloc(sizeof(*wsi-\u003eh2.h2n), \u0022h2n\u0022);\n\tif (!wsi-\u003eh2.h2n)\n\t\treturn 1;\n\n\tlws_h2_init(wsi);\n\n\t/* HTTP2 union */\n\n\tif (lws_hpack_dynamic_size(wsi,\n\t\t\t (int)wsi-\u003eh2.h2n-\u003eour_set.s[H2SET_HEADER_TABLE_SIZE]))\n\t\treturn 1;\n\twsi-\u003etxc.tx_cr \u003d 65535;\n\n\tlwsl_info(\u0022%s: %s: configured for h2\u005cn\u0022, __func__, lws_wsi_tag(wsi));\n\n\treturn 0;\n}\n\nstatic int\nrops_issue_keepalive_h2(struct lws *wsi, int isvalid)\n{\n\tstruct lws *nwsi \u003d lws_get_network_wsi(wsi);\n\tstruct lws_h2_protocol_send *pps;\n\tuint64_t us \u003d (uint64_t)lws_now_usecs();\n\n\tif (isvalid) {\n\t\t_lws_validity_confirmed_role(nwsi);\n\n\t\treturn 0;\n\t}\n\n\t/*\n\t * We can only send these frames on the network connection itself...\n\t * we shouldn't be tracking validity on anything else\n\t */\n\n\tassert(wsi \u003d\u003d nwsi);\n\n\tpps \u003d lws_h2_new_pps(LWS_H2_PPS_PING);\n\tif (!pps)\n\t\treturn 1;\n\n\t/*\n\t * The peer is defined to copy us back the unchanged payload in another\n\t * PING frame this time with ACK set. So by sending that out with the\n\t * current time, it's an interesting opportunity to learn the effective\n\t * RTT on the link when the PONG comes in, plus or minus the time to\n\t * schedule the PPS.\n\t */\n\n\tmemcpy(pps-\u003eu.ping.ping_payload, \u0026us, 8);\n\tlws_pps_schedule(nwsi, pps);\n\n\treturn 0;\n}\n\nstatic const lws_rops_t rops_table_h2[] \u003d {\n#if defined(LWS_WITH_SERVER)\n\t/* 1 */ { .check_upgrades\t \u003d rops_check_upgrades_h2 },\n#else\n\t/* 1 */ { .check_upgrades\t \u003d NULL },\n#endif\n\t/* 2 */ { .pt_init_destroy\t \u003d rops_pt_init_destroy_h2 },\n\t/* 3 */ { .init_vhost\t\t \u003d rops_init_vhost_h2 },\n\t/* 4 */ { .handle_POLLIN\t \u003d rops_handle_POLLIN_h2 },\n\t/* 5 */ { .handle_POLLOUT\t \u003d rops_handle_POLLOUT_h2 },\n\t/* 6 */ { .perform_user_POLLOUT \u003d rops_perform_user_POLLOUT_h2 },\n\t/* 7 */ { .callback_on_writable \u003d rops_callback_on_writable_h2 },\n\t/* 8 */ { .tx_credit\t\t \u003d rops_tx_credit_h2 },\n\t/* 9 */ { .write_role_protocol\t \u003d rops_write_role_protocol_h2 },\n\t/* 10 */ { .encapsulation_parent \u003d rops_encapsulation_parent_h2 },\n\t/* 11 */ { .alpn_negotiated\t \u003d rops_alpn_negotiated_h2 },\n\t/* 12 */ { .close_kill_connection \u003d rops_close_kill_connection_h2 },\n\t/* 13 */ { .destroy_role\t \u003d rops_destroy_role_h2 },\n\t/* 14 */ { .issue_keepalive\t \u003d rops_issue_keepalive_h2 },\n};\n\n\nconst struct lws_role_ops role_ops_h2 \u003d {\n\t/* role name */\t\t\t\u0022h2\u0022,\n\t/* alpn id */\t\t\t\u0022h2\u0022,\n\n\t/* rops_table */\t\trops_table_h2,\n\t/* rops_idx */\t\t\t{\n\t /* LWS_ROPS_check_upgrades */\n#if defined(LWS_WITH_SERVER)\n\t /* LWS_ROPS_pt_init_destroy */\t\t0x12,\n#else\n\t /* LWS_ROPS_pt_init_destroy */\t\t0x02,\n#endif\n\t /* LWS_ROPS_init_vhost */\n\t /* LWS_ROPS_destroy_vhost */\t\t\t0x30,\n\t /* LWS_ROPS_service_flag_pending */\n\t /* LWS_ROPS_handle_POLLIN */\t\t\t0x04,\n\t /* LWS_ROPS_handle_POLLOUT */\n\t /* LWS_ROPS_perform_user_POLLOUT */\t\t0x56,\n\t /* LWS_ROPS_callback_on_writable */\n\t /* LWS_ROPS_tx_credit */\t\t\t0x78,\n\t /* LWS_ROPS_write_role_protocol */\n\t /* LWS_ROPS_encapsulation_parent */\t\t0x9a,\n\t /* LWS_ROPS_alpn_negotiated */\n\t /* LWS_ROPS_close_via_role_protocol */\t0xb0,\n\t /* LWS_ROPS_close_role */\n\t /* LWS_ROPS_close_kill_connection */\t\t0x0c,\n\t /* LWS_ROPS_destroy_role */\n\t /* LWS_ROPS_adoption_bind */\t\t\t0xd0,\n\t /* LWS_ROPS_client_bind */\n\t /* LWS_ROPS_issue_keepalive */\t\t0x0e,\n\t\t\t\t\t},\n\t/* adoption_cb clnt, srv */\t{ LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED,\n\t\t\t\t\t LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED },\n\t/* rx cb clnt, srv */\t\t{ LWS_CALLBACK_RECEIVE_CLIENT_HTTP,\n\t\t\t\t\t 0 /* may be POST, etc */ },\n\t/* writeable cb clnt, srv */\t{ LWS_CALLBACK_CLIENT_HTTP_WRITEABLE,\n\t\t\t\t\t LWS_CALLBACK_HTTP_WRITEABLE },\n\t/* close cb clnt, srv */\t{ LWS_CALLBACK_CLOSED_CLIENT_HTTP,\n\t\t\t\t\t LWS_CALLBACK_CLOSED_HTTP },\n\t/* protocol_bind cb c, srv */\t{ LWS_CALLBACK_CLIENT_HTTP_BIND_PROTOCOL,\n\t\t\t\t\t LWS_CALLBACK_HTTP_BIND_PROTOCOL },\n\t/* protocol_unbind cb c, srv */\t{ LWS_CALLBACK_CLIENT_HTTP_DROP_PROTOCOL,\n\t\t\t\t\t LWS_CALLBACK_HTTP_DROP_PROTOCOL },\n\t/* file_handle */\t\t0,\n};\n","s":{"c":1638757721,"u": 1899}} ],"g": 13538,"chitpc": 0,"ehitpc": 0,"indexed":0 , "ab": 1, "si": 0, "db":0, "di":0, "sat":0, "lfc": "0000"}