| /* |
| * libwebsockets - small server side websockets and web server implementation |
| * |
| * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com> |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a copy |
| * of this software and associated documentation files (the "Software"), to |
| * deal in the Software without restriction, including without limitation the |
| * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
| * sell copies of the Software, and to permit persons to whom the Software is |
| * furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| * IN THE SOFTWARE. |
| */ |
| |
| #include "private-lib-core.h" |
| |
| #if defined (_DEBUG) |
| void lwsi_set_role(struct lws *wsi, lws_wsi_state_t role) |
| { |
| wsi->wsistate = (wsi->wsistate & (~LWSI_ROLE_MASK)) | role; |
| |
| lwsl_debug("lwsi_set_role(%p, 0x%lx)\n", wsi, |
| (unsigned long)wsi->wsistate); |
| } |
| |
| void lwsi_set_state(struct lws *wsi, lws_wsi_state_t lrs) |
| { |
| wsi->wsistate = (wsi->wsistate & (~LRS_MASK)) | lrs; |
| |
| lwsl_debug("lwsi_set_state(%p, 0x%lx)\n", wsi, |
| (unsigned long)wsi->wsistate); |
| } |
| #endif |
| |
| |
| void |
| lws_vhost_bind_wsi(struct lws_vhost *vh, struct lws *wsi) |
| { |
| if (wsi->vhost == vh) |
| return; |
| lws_context_lock(vh->context, __func__); /* ---------- context { */ |
| wsi->vhost = vh; |
| vh->count_bound_wsi++; |
| lws_context_unlock(vh->context); /* } context ---------- */ |
| lwsl_info("%s: vh %s: count_bound_wsi %d\n", |
| __func__, vh->name, vh->count_bound_wsi); |
| assert(wsi->vhost->count_bound_wsi > 0); |
| } |
| |
| void |
| lws_vhost_unbind_wsi(struct lws *wsi) |
| { |
| if (!wsi->vhost) |
| return; |
| |
| lws_context_lock(wsi->context, __func__); /* ---------- context { */ |
| |
| assert(wsi->vhost->count_bound_wsi > 0); |
| wsi->vhost->count_bound_wsi--; |
| lwsl_info("%s: vh %s: count_bound_wsi %d\n", __func__, |
| wsi->vhost->name, wsi->vhost->count_bound_wsi); |
| |
| if (!wsi->vhost->count_bound_wsi && |
| wsi->vhost->being_destroyed) { |
| /* |
| * We have closed all wsi that were bound to this vhost |
| * by any pt: nothing can be servicing any wsi belonging |
| * to it any more. |
| * |
| * Finalize the vh destruction |
| */ |
| __lws_vhost_destroy2(wsi->vhost); |
| } |
| wsi->vhost = NULL; |
| |
| lws_context_unlock(wsi->context); /* } context ---------- */ |
| } |
| |
| LWS_VISIBLE struct lws * |
| lws_get_network_wsi(struct lws *wsi) |
| { |
| if (!wsi) |
| return NULL; |
| |
| #if defined(LWS_WITH_HTTP2) |
| if (!wsi->http2_substream |
| #if defined(LWS_WITH_CLIENT) |
| && !wsi->client_h2_substream |
| #endif |
| ) |
| return wsi; |
| |
| while (wsi->h2.parent_wsi) |
| wsi = wsi->h2.parent_wsi; |
| #endif |
| |
| return wsi; |
| } |
| |
| |
| LWS_VISIBLE LWS_EXTERN const struct lws_protocols * |
| lws_vhost_name_to_protocol(struct lws_vhost *vh, const char *name) |
| { |
| int n; |
| |
| for (n = 0; n < vh->count_protocols; n++) |
| if (vh->protocols[n].name && !strcmp(name, vh->protocols[n].name)) |
| return &vh->protocols[n]; |
| |
| return NULL; |
| } |
| |
| LWS_VISIBLE int |
| lws_callback_all_protocol(struct lws_context *context, |
| const struct lws_protocols *protocol, int reason) |
| { |
| struct lws_context_per_thread *pt = &context->pt[0]; |
| unsigned int n, m = context->count_threads; |
| struct lws *wsi; |
| |
| while (m--) { |
| for (n = 0; n < pt->fds_count; n++) { |
| wsi = wsi_from_fd(context, pt->fds[n].fd); |
| if (!wsi) |
| continue; |
| if (wsi->protocol == protocol) |
| protocol->callback(wsi, reason, wsi->user_space, |
| NULL, 0); |
| } |
| pt++; |
| } |
| |
| return 0; |
| } |
| |
| LWS_VISIBLE int |
| lws_callback_all_protocol_vhost_args(struct lws_vhost *vh, |
| const struct lws_protocols *protocol, int reason, |
| void *argp, size_t len) |
| { |
| struct lws_context *context = vh->context; |
| struct lws_context_per_thread *pt = &context->pt[0]; |
| unsigned int n, m = context->count_threads; |
| struct lws *wsi; |
| |
| while (m--) { |
| for (n = 0; n < pt->fds_count; n++) { |
| wsi = wsi_from_fd(context, pt->fds[n].fd); |
| if (!wsi) |
| continue; |
| if (wsi->vhost == vh && (wsi->protocol == protocol || |
| !protocol)) |
| wsi->protocol->callback(wsi, reason, |
| wsi->user_space, argp, len); |
| } |
| pt++; |
| } |
| |
| return 0; |
| } |
| |
| LWS_VISIBLE int |
| lws_callback_all_protocol_vhost(struct lws_vhost *vh, |
| const struct lws_protocols *protocol, int reason) |
| { |
| return lws_callback_all_protocol_vhost_args(vh, protocol, reason, NULL, 0); |
| } |
| |
| LWS_VISIBLE LWS_EXTERN int |
| lws_callback_vhost_protocols(struct lws *wsi, int reason, void *in, int len) |
| { |
| int n; |
| |
| for (n = 0; n < wsi->vhost->count_protocols; n++) |
| if (wsi->vhost->protocols[n].callback(wsi, reason, NULL, in, len)) |
| return 1; |
| |
| return 0; |
| } |
| |
| LWS_VISIBLE LWS_EXTERN int |
| lws_callback_vhost_protocols_vhost(struct lws_vhost *vh, int reason, void *in, |
| size_t len) |
| { |
| int n; |
| struct lws *wsi = lws_zalloc(sizeof(*wsi), "fake wsi"); |
| |
| if (!wsi) |
| return 1; |
| |
| wsi->context = vh->context; |
| lws_vhost_bind_wsi(vh, wsi); |
| |
| for (n = 0; n < wsi->vhost->count_protocols; n++) { |
| wsi->protocol = &vh->protocols[n]; |
| if (wsi->protocol->callback(wsi, reason, NULL, in, len)) { |
| lws_free(wsi); |
| return 1; |
| } |
| } |
| |
| lws_free(wsi); |
| |
| return 0; |
| } |
| |
| |
| LWS_VISIBLE int |
| lws_rx_flow_control(struct lws *wsi, int _enable) |
| { |
| struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; |
| int en = _enable; |
| |
| // h2 ignores rx flow control atm |
| if (lwsi_role_h2(wsi) || wsi->http2_substream || |
| lwsi_role_h2_ENCAPSULATION(wsi)) |
| return 0; // !!! |
| |
| lwsl_info("%s: %p 0x%x\n", __func__, wsi, _enable); |
| |
| if (!(_enable & LWS_RXFLOW_REASON_APPLIES)) { |
| /* |
| * convert user bool style to bitmap style... in user simple |
| * bool style _enable = 0 = flow control it, = 1 = allow rx |
| */ |
| en = LWS_RXFLOW_REASON_APPLIES | LWS_RXFLOW_REASON_USER_BOOL; |
| if (_enable & 1) |
| en |= LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT; |
| } |
| |
| lws_pt_lock(pt, __func__); |
| |
| /* any bit set in rxflow_bitmap DISABLEs rxflow control */ |
| if (en & LWS_RXFLOW_REASON_APPLIES_ENABLE_BIT) |
| wsi->rxflow_bitmap &= ~(en & 0xff); |
| else |
| wsi->rxflow_bitmap |= en & 0xff; |
| |
| if ((LWS_RXFLOW_PENDING_CHANGE | (!wsi->rxflow_bitmap)) == |
| wsi->rxflow_change_to) |
| goto skip; |
| |
| wsi->rxflow_change_to = LWS_RXFLOW_PENDING_CHANGE | |
| (!wsi->rxflow_bitmap); |
| |
| lwsl_info("%s: %p: bitmap 0x%x: en 0x%x, ch 0x%x\n", __func__, wsi, |
| wsi->rxflow_bitmap, en, wsi->rxflow_change_to); |
| |
| if (_enable & LWS_RXFLOW_REASON_FLAG_PROCESS_NOW || |
| !wsi->rxflow_will_be_applied) { |
| en = __lws_rx_flow_control(wsi); |
| lws_pt_unlock(pt); |
| |
| return en; |
| } |
| |
| skip: |
| lws_pt_unlock(pt); |
| |
| return 0; |
| } |
| |
| LWS_VISIBLE void |
| lws_rx_flow_allow_all_protocol(const struct lws_context *context, |
| const struct lws_protocols *protocol) |
| { |
| const struct lws_context_per_thread *pt = &context->pt[0]; |
| struct lws *wsi; |
| unsigned int n, m = context->count_threads; |
| |
| while (m--) { |
| for (n = 0; n < pt->fds_count; n++) { |
| wsi = wsi_from_fd(context, pt->fds[n].fd); |
| if (!wsi) |
| continue; |
| if (wsi->protocol == protocol) |
| lws_rx_flow_control(wsi, LWS_RXFLOW_ALLOW); |
| } |
| pt++; |
| } |
| } |
| |
| int user_callback_handle_rxflow(lws_callback_function callback_function, |
| struct lws *wsi, |
| enum lws_callback_reasons reason, void *user, |
| void *in, size_t len) |
| { |
| int n; |
| |
| wsi->rxflow_will_be_applied = 1; |
| n = callback_function(wsi, reason, user, in, len); |
| wsi->rxflow_will_be_applied = 0; |
| if (!n) |
| n = __lws_rx_flow_control(wsi); |
| |
| return n; |
| } |
| |
| LWS_EXTERN int |
| __lws_rx_flow_control(struct lws *wsi) |
| { |
| struct lws *wsic = wsi->child_list; |
| |
| // h2 ignores rx flow control atm |
| if (lwsi_role_h2(wsi) || wsi->http2_substream || |
| lwsi_role_h2_ENCAPSULATION(wsi)) |
| return 0; // !!! |
| |
| /* if he has children, do those if they were changed */ |
| while (wsic) { |
| if (wsic->rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE) |
| __lws_rx_flow_control(wsic); |
| |
| wsic = wsic->sibling_list; |
| } |
| |
| /* there is no pending change */ |
| if (!(wsi->rxflow_change_to & LWS_RXFLOW_PENDING_CHANGE)) |
| return 0; |
| |
| /* stuff is still buffered, not ready to really accept new input */ |
| if (lws_buflist_next_segment_len(&wsi->buflist, NULL)) { |
| /* get ourselves called back to deal with stashed buffer */ |
| lws_callback_on_writable(wsi); |
| // return 0; |
| } |
| |
| /* now the pending is cleared, we can change rxflow state */ |
| |
| wsi->rxflow_change_to &= ~LWS_RXFLOW_PENDING_CHANGE; |
| |
| lwsl_info("rxflow: wsi %p change_to %d\n", wsi, |
| wsi->rxflow_change_to & LWS_RXFLOW_ALLOW); |
| |
| /* adjust the pollfd for this wsi */ |
| |
| if (wsi->rxflow_change_to & LWS_RXFLOW_ALLOW) { |
| lwsl_info("%s: reenable POLLIN\n", __func__); |
| // lws_buflist_describe(&wsi->buflist, NULL); |
| if (__lws_change_pollfd(wsi, 0, LWS_POLLIN)) { |
| lwsl_info("%s: fail\n", __func__); |
| return -1; |
| } |
| } else |
| if (__lws_change_pollfd(wsi, LWS_POLLIN, 0)) |
| return -1; |
| |
| return 0; |
| } |
| |
| |
| LWS_VISIBLE const struct lws_protocols * |
| lws_get_protocol(struct lws *wsi) |
| { |
| return wsi->protocol; |
| } |
| |
| |
| int |
| lws_ensure_user_space(struct lws *wsi) |
| { |
| if (!wsi->protocol) |
| return 0; |
| |
| /* allocate the per-connection user memory (if any) */ |
| |
| if (wsi->protocol->per_session_data_size && !wsi->user_space) { |
| wsi->user_space = lws_zalloc( |
| wsi->protocol->per_session_data_size, "user space"); |
| if (wsi->user_space == NULL) { |
| lwsl_err("%s: OOM\n", __func__); |
| return 1; |
| } |
| } else |
| lwsl_debug("%s: %p protocol pss %lu, user_space=%p\n", __func__, |
| wsi, (long)wsi->protocol->per_session_data_size, |
| wsi->user_space); |
| return 0; |
| } |
| |
| LWS_VISIBLE void * |
| lws_adjust_protocol_psds(struct lws *wsi, size_t new_size) |
| { |
| ((struct lws_protocols *)lws_get_protocol(wsi))->per_session_data_size = |
| new_size; |
| |
| if (lws_ensure_user_space(wsi)) |
| return NULL; |
| |
| return wsi->user_space; |
| } |
| |
| |
| |
| LWS_VISIBLE int |
| lws_is_ssl(struct lws *wsi) |
| { |
| #if defined(LWS_WITH_TLS) |
| return wsi->tls.use_ssl & LCCSCF_USE_SSL; |
| #else |
| (void)wsi; |
| return 0; |
| #endif |
| } |
| |
| #if defined(LWS_WITH_TLS) && !defined(LWS_WITH_MBEDTLS) |
| LWS_VISIBLE lws_tls_conn* |
| lws_get_ssl(struct lws *wsi) |
| { |
| return wsi->tls.ssl; |
| } |
| #endif |
| |
| LWS_VISIBLE int |
| lws_partial_buffered(struct lws *wsi) |
| { |
| return lws_has_buffered_out(wsi); |
| } |
| |
| LWS_VISIBLE lws_fileofs_t |
| lws_get_peer_write_allowance(struct lws *wsi) |
| { |
| if (!wsi->role_ops->tx_credit) |
| return -1; |
| return wsi->role_ops->tx_credit(wsi); |
| } |
| |
| LWS_VISIBLE void |
| lws_role_transition(struct lws *wsi, enum lwsi_role role, enum lwsi_state state, |
| const struct lws_role_ops *ops) |
| { |
| #if defined(_DEBUG) |
| const char *name = "(unset)"; |
| #endif |
| wsi->wsistate = role | state; |
| if (ops) |
| wsi->role_ops = ops; |
| #if defined(_DEBUG) |
| if (wsi->role_ops) |
| name = wsi->role_ops->name; |
| lwsl_debug("%s: %p: wsistate 0x%lx, ops %s\n", __func__, wsi, |
| (unsigned long)wsi->wsistate, name); |
| #endif |
| } |
| |
| LWS_VISIBLE LWS_EXTERN int |
| lws_parse_uri(char *p, const char **prot, const char **ads, int *port, |
| const char **path) |
| { |
| const char *end; |
| char unix_skt = 0; |
| |
| /* cut up the location into address, port and path */ |
| *prot = p; |
| while (*p && (*p != ':' || p[1] != '/' || p[2] != '/')) |
| p++; |
| if (!*p) { |
| end = p; |
| p = (char *)*prot; |
| *prot = end; |
| } else { |
| *p = '\0'; |
| p += 3; |
| } |
| if (*p == '+') /* unix skt */ |
| unix_skt = 1; |
| |
| *ads = p; |
| if (!strcmp(*prot, "http") || !strcmp(*prot, "ws")) |
| *port = 80; |
| else if (!strcmp(*prot, "https") || !strcmp(*prot, "wss")) |
| *port = 443; |
| |
| if (*p == '[') { |
| ++(*ads); |
| while (*p && *p != ']') |
| p++; |
| if (*p) |
| *p++ = '\0'; |
| } else |
| while (*p && *p != ':' && (unix_skt || *p != '/')) |
| p++; |
| |
| if (*p == ':') { |
| *p++ = '\0'; |
| *port = atoi(p); |
| while (*p && *p != '/') |
| p++; |
| } |
| *path = "/"; |
| if (*p) { |
| *p++ = '\0'; |
| if (*p) |
| *path = p; |
| } |
| |
| return 0; |
| } |
| |
| /* ... */ |
| |
| LWS_VISIBLE LWS_EXTERN const char * |
| lws_get_urlarg_by_name(struct lws *wsi, const char *name, char *buf, int len) |
| { |
| int n = 0, sl = (int)strlen(name); |
| |
| while (lws_hdr_copy_fragment(wsi, buf, len, |
| WSI_TOKEN_HTTP_URI_ARGS, n) >= 0) { |
| |
| if (!strncmp(buf, name, sl)) |
| return buf + sl; |
| |
| n++; |
| } |
| |
| return NULL; |
| } |
| |
| |
| #if defined(LWS_WITHOUT_EXTENSIONS) |
| |
| /* we need to provide dummy callbacks for internal exts |
| * so user code runs when faced with a lib compiled with |
| * extensions disabled. |
| */ |
| |
| LWS_VISIBLE int |
| lws_extension_callback_pm_deflate(struct lws_context *context, |
| const struct lws_extension *ext, |
| struct lws *wsi, |
| enum lws_extension_callback_reasons reason, |
| void *user, void *in, size_t len) |
| { |
| (void)context; |
| (void)ext; |
| (void)wsi; |
| (void)reason; |
| (void)user; |
| (void)in; |
| (void)len; |
| |
| return 0; |
| } |
| |
| LWS_EXTERN int |
| lws_set_extension_option(struct lws *wsi, const char *ext_name, |
| const char *opt_name, const char *opt_val) |
| { |
| return -1; |
| } |
| #endif |
| |
| LWS_VISIBLE LWS_EXTERN int |
| lws_is_cgi(struct lws *wsi) { |
| #ifdef LWS_WITH_CGI |
| return !!wsi->http.cgi; |
| #else |
| return 0; |
| #endif |
| } |
| |
| const struct lws_protocol_vhost_options * |
| lws_pvo_search(const struct lws_protocol_vhost_options *pvo, const char *name) |
| { |
| while (pvo) { |
| if (!strcmp(pvo->name, name)) |
| break; |
| |
| pvo = pvo->next; |
| } |
| |
| return pvo; |
| } |
| |
| int |
| lws_pvo_get_str(void *in, const char *name, const char **result) |
| { |
| const struct lws_protocol_vhost_options *pv = |
| lws_pvo_search((const struct lws_protocol_vhost_options *)in, |
| name); |
| |
| if (!pv) |
| return 1; |
| |
| *result = (const char *)pv->value; |
| |
| return 0; |
| } |
| |
| int |
| lws_broadcast(struct lws_context_per_thread *pt, int reason, void *in, size_t len) |
| { |
| struct lws_vhost *v = pt->context->vhost_list; |
| int n, ret = 0; |
| |
| pt->fake_wsi->context = pt->context; |
| |
| while (v) { |
| const struct lws_protocols *p = v->protocols; |
| pt->fake_wsi->vhost = v; /* not a real bound wsi */ |
| |
| for (n = 0; n < v->count_protocols; n++) { |
| pt->fake_wsi->protocol = p; |
| if (p->callback && |
| p->callback(pt->fake_wsi, reason, NULL, in, len)) |
| ret |= 1; |
| p++; |
| } |
| v = v->vhost_next; |
| } |
| |
| return ret; |
| } |
| |
| LWS_VISIBLE LWS_EXTERN void * |
| lws_wsi_user(struct lws *wsi) |
| { |
| return wsi->user_space; |
| } |
| |
| LWS_VISIBLE LWS_EXTERN void |
| lws_set_wsi_user(struct lws *wsi, void *data) |
| { |
| if (wsi->user_space_externally_allocated) |
| wsi->user_space = data; |
| else |
| lwsl_err("%s: Cannot set internally-allocated user_space\n", |
| __func__); |
| } |
| |
| LWS_VISIBLE LWS_EXTERN struct lws * |
| lws_get_parent(const struct lws *wsi) |
| { |
| return wsi->parent; |
| } |
| |
| LWS_VISIBLE LWS_EXTERN struct lws * |
| lws_get_child(const struct lws *wsi) |
| { |
| return wsi->child_list; |
| } |
| |
| LWS_VISIBLE LWS_EXTERN void * |
| lws_get_opaque_parent_data(const struct lws *wsi) |
| { |
| return wsi->opaque_parent_data; |
| } |
| |
| LWS_VISIBLE LWS_EXTERN void |
| lws_set_opaque_parent_data(struct lws *wsi, void *data) |
| { |
| wsi->opaque_parent_data = data; |
| } |
| |
| LWS_VISIBLE LWS_EXTERN void * |
| lws_get_opaque_user_data(const struct lws *wsi) |
| { |
| return wsi->opaque_user_data; |
| } |
| |
| LWS_VISIBLE LWS_EXTERN void |
| lws_set_opaque_user_data(struct lws *wsi, void *data) |
| { |
| wsi->opaque_user_data = data; |
| } |
| |
| LWS_VISIBLE LWS_EXTERN int |
| lws_get_child_pending_on_writable(const struct lws *wsi) |
| { |
| return wsi->parent_pending_cb_on_writable; |
| } |
| |
| LWS_VISIBLE LWS_EXTERN void |
| lws_clear_child_pending_on_writable(struct lws *wsi) |
| { |
| wsi->parent_pending_cb_on_writable = 0; |
| } |
| |
| |
| |
| LWS_VISIBLE LWS_EXTERN const char * |
| lws_get_vhost_name(struct lws_vhost *vhost) |
| { |
| return vhost->name; |
| } |
| |
| LWS_VISIBLE LWS_EXTERN int |
| lws_get_vhost_port(struct lws_vhost *vhost) |
| { |
| return vhost->listen_port; |
| } |
| |
| LWS_VISIBLE LWS_EXTERN void * |
| lws_get_vhost_user(struct lws_vhost *vhost) |
| { |
| return vhost->user; |
| } |
| |
| LWS_VISIBLE LWS_EXTERN const char * |
| lws_get_vhost_iface(struct lws_vhost *vhost) |
| { |
| return vhost->iface; |
| } |
| |
| LWS_VISIBLE lws_sockfd_type |
| lws_get_socket_fd(struct lws *wsi) |
| { |
| if (!wsi) |
| return -1; |
| return wsi->desc.sockfd; |
| } |
| |
| |
| LWS_VISIBLE struct lws_vhost * |
| lws_vhost_get(struct lws *wsi) |
| { |
| return wsi->vhost; |
| } |
| |
| LWS_VISIBLE struct lws_vhost * |
| lws_get_vhost(struct lws *wsi) |
| { |
| return wsi->vhost; |
| } |
| |
| LWS_VISIBLE const struct lws_protocols * |
| lws_protocol_get(struct lws *wsi) |
| { |
| return wsi->protocol; |
| } |
| |
| LWS_VISIBLE const struct lws_udp * |
| lws_get_udp(const struct lws *wsi) |
| { |
| return wsi->udp; |
| } |
| |
| LWS_VISIBLE LWS_EXTERN struct lws_context * |
| lws_get_context(const struct lws *wsi) |
| { |
| return wsi->context; |
| } |
| |
| #ifdef LWS_LATENCY |
| void |
| lws_latency(struct lws_context *context, struct lws *wsi, const char *action, |
| int ret, int completed) |
| { |
| unsigned long long u; |
| char buf[256]; |
| |
| u = lws_now_usecs(); |
| |
| if (!action) { |
| wsi->latency_start = u; |
| if (!wsi->action_start) |
| wsi->action_start = u; |
| return; |
| } |
| if (completed) { |
| if (wsi->action_start == wsi->latency_start) |
| sprintf(buf, |
| "Completion first try lat %lluus: %p: ret %d: %s\n", |
| u - wsi->latency_start, |
| (void *)wsi, ret, action); |
| else |
| sprintf(buf, |
| "Completion %lluus: lat %lluus: %p: ret %d: %s\n", |
| u - wsi->action_start, |
| u - wsi->latency_start, |
| (void *)wsi, ret, action); |
| wsi->action_start = 0; |
| } else |
| sprintf(buf, "lat %lluus: %p: ret %d: %s\n", |
| u - wsi->latency_start, (void *)wsi, ret, action); |
| |
| if (u - wsi->latency_start > context->worst_latency) { |
| context->worst_latency = u - wsi->latency_start; |
| strcpy(context->worst_latency_info, buf); |
| } |
| lwsl_latency("%s", buf); |
| } |
| #endif |
| |
| LWS_VISIBLE int LWS_WARN_UNUSED_RESULT |
| lws_raw_transaction_completed(struct lws *wsi) |
| { |
| if (lws_has_buffered_out(wsi)) { |
| /* |
| * ...so he tried to send something large, but it went out |
| * as a partial, but he immediately called us to say he wants |
| * to close the connection. |
| * |
| * Defer the close until the last part of the partial is sent. |
| * |
| */ |
| lwsl_debug("%s: %p: deferring due to partial\n", __func__, wsi); |
| wsi->close_when_buffered_out_drained = 1; |
| lws_callback_on_writable(wsi); |
| |
| return 0; |
| } |
| |
| return -1; |
| } |
| |
| int |
| lws_bind_protocol(struct lws *wsi, const struct lws_protocols *p, |
| const char *reason) |
| { |
| // if (wsi->protocol == p) |
| // return 0; |
| const struct lws_protocols *vp = wsi->vhost->protocols, *vpo; |
| |
| if (wsi->protocol && wsi->protocol_bind_balance) { |
| wsi->protocol->callback(wsi, |
| wsi->role_ops->protocol_unbind_cb[!!lwsi_role_server(wsi)], |
| wsi->user_space, (void *)reason, 0); |
| wsi->protocol_bind_balance = 0; |
| } |
| if (!wsi->user_space_externally_allocated) |
| lws_free_set_NULL(wsi->user_space); |
| |
| lws_same_vh_protocol_remove(wsi); |
| |
| wsi->protocol = p; |
| if (!p) |
| return 0; |
| |
| if (lws_ensure_user_space(wsi)) |
| return 1; |
| |
| if (p > vp && p < &vp[wsi->vhost->count_protocols]) |
| lws_same_vh_protocol_insert(wsi, (int)(p - vp)); |
| else { |
| int n = wsi->vhost->count_protocols; |
| int hit = 0; |
| |
| vpo = vp; |
| |
| while (n--) { |
| if (p->name && vp->name && !strcmp(p->name, vp->name)) { |
| hit = 1; |
| lws_same_vh_protocol_insert(wsi, (int)(vp - vpo)); |
| break; |
| } |
| vp++; |
| } |
| if (!hit) |
| lwsl_err("%s: %p is not in vhost '%s' protocols list\n", |
| __func__, p, wsi->vhost->name); |
| } |
| |
| if (wsi->protocol->callback(wsi, wsi->role_ops->protocol_bind_cb[ |
| !!lwsi_role_server(wsi)], |
| wsi->user_space, NULL, 0)) |
| return 1; |
| |
| wsi->protocol_bind_balance = 1; |
| |
| return 0; |
| } |
| |
| int |
| lws_http_mark_sse(struct lws *wsi) |
| { |
| lws_http_headers_detach(wsi); |
| lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); |
| |
| if (wsi->http2_substream) { |
| struct lws *nwsi = lws_get_network_wsi(wsi); |
| |
| wsi->h2_stream_carries_sse = 1; |
| nwsi->immortal_substream_count++; |
| if (nwsi->immortal_substream_count == 1) |
| lws_set_timeout(nwsi, NO_PENDING_TIMEOUT, 0); |
| } |
| |
| return 0; |
| } |