void lws_set_protocol_write_pending(struct libwebsocket_context *context, struct libwebsocket *wsi, enum lws_pending_protocol_send pend) { lwsl_info("setting pps %d\n", pend); if (wsi->pps) lwsl_err("pps overwrite\n"); wsi->pps = pend; libwebsocket_rx_flow_control(wsi, 0); libwebsocket_callback_on_writable(context, wsi); }
LWS_VISIBLE void libwebsocket_rx_flow_allow_all_protocol( const struct libwebsocket_protocols *protocol) { struct libwebsocket_context *context = protocol->owning_server; int n; struct libwebsocket *wsi; for (n = 0; n < context->fds_count; n++) { wsi = wsi_from_fd(context, context->fds[n].fd); if (!wsi) continue; if (wsi->protocol == protocol) libwebsocket_rx_flow_control(wsi, LWS_RXFLOW_ALLOW); } }
static int callback_lws_mirror(struct libwebsocket_context *context, struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, void *user, void *in, size_t len) { int n; struct per_session_data__lws_mirror *pss = (struct per_session_data__lws_mirror *)user; switch (reason) { case LWS_CALLBACK_ESTABLISHED: lwsl_info("callback_lws_mirror: LWS_CALLBACK_ESTABLISHED\n"); pss->ringbuffer_tail = ringbuffer_head; pss->wsi = wsi; break; case LWS_CALLBACK_PROTOCOL_DESTROY: lwsl_notice("mirror protocol cleaning up\n"); for (n = 0; n < sizeof ringbuffer / sizeof ringbuffer[0]; n++) if (ringbuffer[n].payload) free(ringbuffer[n].payload); break; case LWS_CALLBACK_SERVER_WRITEABLE: if (close_testing) break; while (pss->ringbuffer_tail != ringbuffer_head) { n = libwebsocket_write(wsi, (unsigned char *) ringbuffer[pss->ringbuffer_tail].payload + LWS_SEND_BUFFER_PRE_PADDING, ringbuffer[pss->ringbuffer_tail].len, LWS_WRITE_TEXT); if (n < 0) { lwsl_err("ERROR %d writing to mirror socket\n", n); return -1; } if (n < ringbuffer[pss->ringbuffer_tail].len) lwsl_err("mirror partial write %d vs %d\n", n, ringbuffer[pss->ringbuffer_tail].len); if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1)) pss->ringbuffer_tail = 0; else pss->ringbuffer_tail++; if (((ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 15)) libwebsocket_rx_flow_allow_all_protocol( libwebsockets_get_protocol(wsi)); // lwsl_debug("tx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1)); if (lws_send_pipe_choked(wsi)) { libwebsocket_callback_on_writable(context, wsi); break; } /* * for tests with chrome on same machine as client and * server, this is needed to stop chrome choking */ #ifdef _WIN32 Sleep(1); #else usleep(1); #endif } break; case LWS_CALLBACK_RECEIVE: if (((ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 1)) { lwsl_err("dropping!\n"); goto choke; } if (ringbuffer[ringbuffer_head].payload) free(ringbuffer[ringbuffer_head].payload); ringbuffer[ringbuffer_head].payload = malloc(LWS_SEND_BUFFER_PRE_PADDING + len + LWS_SEND_BUFFER_POST_PADDING); ringbuffer[ringbuffer_head].len = len; memcpy((char *)ringbuffer[ringbuffer_head].payload + LWS_SEND_BUFFER_PRE_PADDING, in, len); if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1)) ringbuffer_head = 0; else ringbuffer_head++; if (((ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1)) != (MAX_MESSAGE_QUEUE - 2)) goto done; choke: lwsl_debug("LWS_CALLBACK_RECEIVE: throttling %p\n", wsi); libwebsocket_rx_flow_control(wsi, 0); // lwsl_debug("rx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1)); done: libwebsocket_callback_on_writable_all_protocol( libwebsockets_get_protocol(wsi)); break; /* * this just demonstrates how to use the protocol filter. If you won't * study and reject connections based on header content, you don't need * to handle this callback */ case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION: dump_handshake_info(wsi); /* you could return non-zero here and kill the connection */ break; default: break; } return 0; }
static int callback_lws_mirror(struct libwebsocket_context *context, struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, void *user, void *in, size_t len) { int n; struct per_session_data__lws_mirror *pss = user; switch (reason) { case LWS_CALLBACK_ESTABLISHED: fprintf(stderr, "callback_lws_mirror: " "LWS_CALLBACK_ESTABLISHED\n"); pss->ringbuffer_tail = ringbuffer_head; pss->wsi = wsi; break; case LWS_CALLBACK_SERVER_WRITEABLE: if (close_testing) break; if (pss->ringbuffer_tail != ringbuffer_head) { n = libwebsocket_write(wsi, (unsigned char *) ringbuffer[pss->ringbuffer_tail].payload + LWS_SEND_BUFFER_PRE_PADDING, ringbuffer[pss->ringbuffer_tail].len, LWS_WRITE_TEXT); if (n < 0) { fprintf(stderr, "ERROR writing to socket"); exit(1); } if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1)) pss->ringbuffer_tail = 0; else pss->ringbuffer_tail++; if (((ringbuffer_head - pss->ringbuffer_tail) % MAX_MESSAGE_QUEUE) < (MAX_MESSAGE_QUEUE - 15)) libwebsocket_rx_flow_control(wsi, 1); libwebsocket_callback_on_writable(context, wsi); } break; case LWS_CALLBACK_BROADCAST: n = libwebsocket_write(wsi, in, len, LWS_WRITE_TEXT); if (n < 0) fprintf(stderr, "mirror write failed\n"); break; case LWS_CALLBACK_RECEIVE: if (ringbuffer[ringbuffer_head].payload) free(ringbuffer[ringbuffer_head].payload); ringbuffer[ringbuffer_head].payload = malloc(LWS_SEND_BUFFER_PRE_PADDING + len + LWS_SEND_BUFFER_POST_PADDING); ringbuffer[ringbuffer_head].len = len; memcpy((char *)ringbuffer[ringbuffer_head].payload + LWS_SEND_BUFFER_PRE_PADDING, in, len); if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1)) ringbuffer_head = 0; else ringbuffer_head++; if (((ringbuffer_head - pss->ringbuffer_tail) % MAX_MESSAGE_QUEUE) > (MAX_MESSAGE_QUEUE - 10)) libwebsocket_rx_flow_control(wsi, 0); libwebsocket_callback_on_writable_all_protocol( libwebsockets_get_protocol(wsi)); break; /* * this just demonstrates how to use the protocol filter. If you won't * study and reject connections based on header content, you don't need * to handle this callback */ case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION: dump_handshake_info((struct lws_tokens *)(long)user); /* you could return non-zero here and kill the connection */ break; default: break; } return 0; }
int bmx_libwebsocket_rx_flow_control(struct libwebsocket * wsi, int enable) { return libwebsocket_rx_flow_control(wsi, enable); }
int lws_handle_POLLOUT_event(struct libwebsocket_context *context, struct libwebsocket *wsi, struct libwebsocket_pollfd *pollfd) { int n; struct lws_tokens eff_buf; #ifdef LWS_USE_HTTP2 struct libwebsocket *wsi2; #endif int ret; int m; int handled = 0; /* pending truncated sends have uber priority */ if (wsi->truncated_send_len) { if (lws_issue_raw(wsi, wsi->truncated_send_malloc + wsi->truncated_send_offset, wsi->truncated_send_len) < 0) { lwsl_info("lws_handle_POLLOUT_event signalling to close\n"); return -1; } /* leave POLLOUT active either way */ return 0; } else if (wsi->state == WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE) { lwsl_info("***** %x signalling to close in POLLOUT handler\n", wsi); return -1; /* retry closing now */ } #ifdef LWS_USE_HTTP2 /* protocol packets are next */ if (wsi->pps) { lwsl_info("servicing pps %d\n", wsi->pps); switch (wsi->pps) { case LWS_PPS_HTTP2_MY_SETTINGS: case LWS_PPS_HTTP2_ACK_SETTINGS: lws_http2_do_pps_send(context, wsi); break; default: break; } wsi->pps = LWS_PPS_NONE; libwebsocket_rx_flow_control(wsi, 1); return 0; /* leave POLLOUT active */ } #endif /* pending control packets have next priority */ if (wsi->state == WSI_STATE_ESTABLISHED && wsi->u.ws.ping_payload_len) { n = libwebsocket_write(wsi, &wsi->u.ws.ping_payload_buf[ LWS_SEND_BUFFER_PRE_PADDING], wsi->u.ws.ping_payload_len, LWS_WRITE_PONG); if (n < 0) return -1; /* well he is sent, mark him done */ wsi->u.ws.ping_payload_len = 0; /* leave POLLOUT active either way */ return 0; } /* if nothing critical, user can get the callback */ m = lws_ext_callback_for_each_active(wsi, LWS_EXT_CALLBACK_IS_WRITEABLE, NULL, 0); #ifndef LWS_NO_EXTENSIONS if (!wsi->extension_data_pending || handled == 2) goto user_service; #endif /* * check in on the active extensions, see if they * had pending stuff to spill... they need to get the * first look-in otherwise sequence will be disordered * * NULL, zero-length eff_buf means just spill pending */ ret = 1; while (ret == 1) { /* default to nobody has more to spill */ ret = 0; eff_buf.token = NULL; eff_buf.token_len = 0; /* give every extension a chance to spill */ m = lws_ext_callback_for_each_active(wsi, LWS_EXT_CALLBACK_PACKET_TX_PRESEND, &eff_buf, 0); if (m < 0) { lwsl_err("ext reports fatal error\n"); return -1; } if (m) /* * at least one extension told us he has more * to spill, so we will go around again after */ ret = 1; /* assuming they gave us something to send, send it */ if (eff_buf.token_len) { n = lws_issue_raw(wsi, (unsigned char *)eff_buf.token, eff_buf.token_len); if (n < 0) { lwsl_info("closing from POLLOUT spill\n"); return -1; } /* * Keep amount spilled small to minimize chance of this */ if (n != eff_buf.token_len) { lwsl_err("Unable to spill ext %d vs %s\n", eff_buf.token_len, n); return -1; } } else continue; /* no extension has more to spill */ if (!ret) continue; /* * There's more to spill from an extension, but we just sent * something... did that leave the pipe choked? */ if (!lws_send_pipe_choked(wsi)) /* no we could add more */ continue; lwsl_info("choked in POLLOUT service\n"); /* * Yes, he's choked. Leave the POLLOUT masked on so we will * come back here when he is unchoked. Don't call the user * callback to enforce ordering of spilling, he'll get called * when we come back here and there's nothing more to spill. */ return 0; } #ifndef LWS_NO_EXTENSIONS wsi->extension_data_pending = 0; user_service: #endif /* one shot */ if (pollfd) { if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { lwsl_info("failled at set pollfd\n"); return 1; } lws_libev_io(context, wsi, LWS_EV_STOP | LWS_EV_WRITE); } #ifdef LWS_USE_HTTP2 /* * we are the 'network wsi' for potentially many muxed child wsi with * no network connection of their own, who have to use us for all their * network actions. So we use a round-robin scheme to share out the * POLLOUT notifications to our children. * * But because any child could exhaust the socket's ability to take * writes, we can only let one child get notified each time. * * In addition children may be closed / deleted / added between POLLOUT * notifications, so we can't hold pointers */ if (wsi->mode != LWS_CONNMODE_HTTP2_SERVING) { lwsl_info("%s: non http2\n", __func__); goto notify; } wsi->u.http2.requested_POLLOUT = 0; if (!wsi->u.http2.initialized) { lwsl_info("pollout on uninitialized http2 conn\n"); return 0; } lwsl_info("%s: doing children\n", __func__); wsi2 = wsi; do { wsi2 = wsi2->u.http2.next_child_wsi; lwsl_info("%s: child %p\n", __func__, wsi2); if (!wsi2) continue; if (!wsi2->u.http2.requested_POLLOUT) continue; wsi2->u.http2.requested_POLLOUT = 0; if (lws_calllback_as_writeable(context, wsi2)) { lwsl_debug("Closing POLLOUT child\n"); libwebsocket_close_and_free_session(context, wsi2, LWS_CLOSE_STATUS_NOSTATUS); } wsi2 = wsi; } while (wsi2 != NULL && !lws_send_pipe_choked(wsi)); lwsl_info("%s: completed\n", __func__); return 0; notify: #endif return lws_calllback_as_writeable(context, wsi); }
static int callback_lws_mirror(struct libwebsocket_context *context, struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, void *user, void *in, size_t len) { int n; struct per_session_data__lws_mirror *pss = (struct per_session_data__lws_mirror *)user; switch (reason) { case LWS_CALLBACK_ESTABLISHED: lwsl_info("callback_lws_mirror: " "LWS_CALLBACK_ESTABLISHED\n"); pss->ringbuffer_tail = ringbuffer_head; pss->wsi = wsi; break; case LWS_CALLBACK_PROTOCOL_DESTROY: lwsl_notice("mirror protocol cleaning up\n"); for (n = 0; n < sizeof ringbuffer / sizeof ringbuffer[0]; n++) if (ringbuffer[n].payload) free(ringbuffer[n].payload); break; case LWS_CALLBACK_SERVER_WRITEABLE: if (close_testing) break; while (pss->ringbuffer_tail != ringbuffer_head) { n = libwebsocket_write(wsi, (unsigned char *) ringbuffer[pss->ringbuffer_tail].payload + LWS_SEND_BUFFER_PRE_PADDING, ringbuffer[pss->ringbuffer_tail].len, LWS_WRITE_TEXT); if (n < 0) { lwsl_err("ERROR %d writing to mirror socket\n", n); return -1; } if (pss->ringbuffer_tail == (MAX_MESSAGE_QUEUE - 1)) pss->ringbuffer_tail = 0; else pss->ringbuffer_tail++; if (((ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 15)) { for (n = 0; n < num_wsi_choked; n++) libwebsocket_rx_flow_control(wsi_choked[n], 1); num_wsi_choked = 0; } // lwsl_debug("tx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1)); if (lws_send_pipe_choked(wsi)) { libwebsocket_callback_on_writable(context, wsi); return 0; } } break; case LWS_CALLBACK_RECEIVE: cout << string((char *) in, len) << endl; if (((ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1)) == (MAX_MESSAGE_QUEUE - 1)) { lwsl_err("dropping!\n"); goto choke; } if (ringbuffer[ringbuffer_head].payload) free(ringbuffer[ringbuffer_head].payload); ringbuffer[ringbuffer_head].payload = malloc(LWS_SEND_BUFFER_PRE_PADDING + len + LWS_SEND_BUFFER_POST_PADDING); ringbuffer[ringbuffer_head].len = len; memcpy((char *)ringbuffer[ringbuffer_head].payload + LWS_SEND_BUFFER_PRE_PADDING, in, len); if (ringbuffer_head == (MAX_MESSAGE_QUEUE - 1)) ringbuffer_head = 0; else ringbuffer_head++; if (((ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1)) != (MAX_MESSAGE_QUEUE - 2)) goto done; choke: if (num_wsi_choked < sizeof wsi_choked / sizeof wsi_choked[0]) { libwebsocket_rx_flow_control(wsi, 0); wsi_choked[num_wsi_choked++] = wsi; } // lwsl_debug("rx fifo %d\n", (ringbuffer_head - pss->ringbuffer_tail) & (MAX_MESSAGE_QUEUE - 1)); done: libwebsocket_callback_on_writable_all_protocol( libwebsockets_get_protocol(wsi)); break; default: break; } return 0; }