LWS_VISIBLE void lws_libev_io(struct lws *wsi, int flags) { struct lws_context *context = lws_get_context(wsi); struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; if (!LWS_LIBEV_ENABLED(context)) return; if (!pt->io_loop_ev || context->being_destroyed) return; assert((flags & (LWS_EV_START | LWS_EV_STOP)) && (flags & (LWS_EV_READ | LWS_EV_WRITE))); if (flags & LWS_EV_START) { if (flags & LWS_EV_WRITE) ev_io_start(pt->io_loop_ev, &wsi->w_write.ev_watcher); if (flags & LWS_EV_READ) ev_io_start(pt->io_loop_ev, &wsi->w_read.ev_watcher); } else { if (flags & LWS_EV_WRITE) ev_io_stop(pt->io_loop_ev, &wsi->w_write.ev_watcher); if (flags & LWS_EV_READ) ev_io_stop(pt->io_loop_ev, &wsi->w_read.ev_watcher); } }
LWS_VISIBLE void lws_libev_io(struct lws *wsi, int flags) { struct lws_context *context = lws_get_context(wsi); if (!LWS_LIBEV_ENABLED(context)) return; if (!context->io_loop) return; assert((flags & (LWS_EV_START | LWS_EV_STOP)) && (flags & (LWS_EV_READ | LWS_EV_WRITE))); if (flags & LWS_EV_START) { if (flags & LWS_EV_WRITE) ev_io_start(context->io_loop, &wsi->w_write.watcher); if (flags & LWS_EV_READ) ev_io_start(context->io_loop, &wsi->w_read.watcher); } else { if (flags & LWS_EV_WRITE) ev_io_stop(context->io_loop, &wsi->w_write.watcher); if (flags & LWS_EV_READ) ev_io_stop(context->io_loop, &wsi->w_read.watcher); } }
LWS_VISIBLE int lws_libev_init_fd_table(struct lws_context *context) { if (!LWS_LIBEV_ENABLED(context)) return 0; context->w_accept.context = context; context->w_sigint.context = context; return 1; }
LWS_VISIBLE void lws_libev_accept(struct lws *new_wsi, int accept_fd) { struct lws_context *context = lws_get_context(new_wsi); struct ev_io *r = &new_wsi->w_read.watcher; struct ev_io *w = &new_wsi->w_write.watcher; if (!LWS_LIBEV_ENABLED(context)) return; new_wsi->w_read.context = context; new_wsi->w_write.context = context; ev_io_init(r, lws_accept_cb, accept_fd, EV_READ); ev_io_init(w, lws_accept_cb, accept_fd, EV_WRITE); }
LWS_VISIBLE int lws_libev_init_fd_table(struct lws_context *context) { int n; if (!LWS_LIBEV_ENABLED(context)) return 0; for (n = 0; n < context->count_threads; n++) { context->pt[n].w_accept.context = context; context->pt[n].w_sigint.context = context; } return 1; }
LWS_VISIBLE void lws_libev_run(const struct lws_context *context) { if (context->io_loop && LWS_LIBEV_ENABLED(context)) ev_run(context->io_loop, 0); }
LWS_VISIBLE void lws_libev_run(const struct lws_context *context, int tsi) { if (context->pt[tsi].io_loop_ev && LWS_LIBEV_ENABLED(context)) ev_run(context->pt[tsi].io_loop_ev, 0); }
int lws_server_socket_service(struct libwebsocket_context *context, struct libwebsocket *wsi, struct libwebsocket_pollfd *pollfd) { struct libwebsocket *new_wsi; int accept_fd; socklen_t clilen; struct sockaddr_in cli_addr; int n; int len; #ifdef LWS_OPENSSL_SUPPORT int m; #ifndef USE_CYASSL BIO *bio; #endif #endif switch (wsi->mode) { case LWS_CONNMODE_HTTP_SERVING: case LWS_CONNMODE_HTTP_SERVING_ACCEPTED: /* handle http headers coming in */ /* pending truncated sends have uber priority */ if (wsi->truncated_send_malloc) { if (pollfd->revents & LWS_POLLOUT) lws_issue_raw(wsi, wsi->truncated_send_malloc + wsi->truncated_send_offset, wsi->truncated_send_len); /* * we can't afford to allow input processing send * something new, so spin around he event loop until * he doesn't have any partials */ break; } /* any incoming data ready? */ if (pollfd->revents & LWS_POLLIN) { len = lws_ssl_capable_read(wsi, context->service_buffer, sizeof(context->service_buffer)); switch (len) { case 0: lwsl_info("lws_server_skt_srv: read 0 len\n"); /* lwsl_info(" state=%d\n", wsi->state); */ if (!wsi->hdr_parsing_completed) free(wsi->u.hdr.ah); /* fallthru */ case LWS_SSL_CAPABLE_ERROR: libwebsocket_close_and_free_session( context, wsi, LWS_CLOSE_STATUS_NOSTATUS); return 0; case LWS_SSL_CAPABLE_MORE_SERVICE: break; } /* hm this may want to send (via HTTP callback for example) */ n = libwebsocket_read(context, wsi, context->service_buffer, len); if (n < 0) /* we closed wsi */ return 0; /* hum he may have used up the writability above */ break; } /* this handles POLLOUT for http serving fragments */ if (!(pollfd->revents & LWS_POLLOUT)) break; /* one shot */ if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) goto fail; #ifdef LWS_USE_LIBEV if (LWS_LIBEV_ENABLED(context)) ev_io_stop(context->io_loop, (struct ev_io *)&wsi->w_write); #endif /* LWS_USE_LIBEV */ if (wsi->state != WSI_STATE_HTTP_ISSUING_FILE) { n = user_callback_handle_rxflow( wsi->protocol->callback, wsi->protocol->owning_server, wsi, LWS_CALLBACK_HTTP_WRITEABLE, wsi->user_space, NULL, 0); if (n < 0) libwebsocket_close_and_free_session( context, wsi, LWS_CLOSE_STATUS_NOSTATUS); break; } /* nonzero for completion or error */ if (libwebsockets_serve_http_file_fragment(context, wsi)) libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); break; case LWS_CONNMODE_SERVER_LISTENER: /* pollin means a client has connected to us then */ if (!(pollfd->revents & LWS_POLLIN)) break; /* listen socket got an unencrypted connection... */ clilen = sizeof(cli_addr); lws_latency_pre(context, wsi); accept_fd = accept(pollfd->fd, (struct sockaddr *)&cli_addr, &clilen); lws_latency(context, wsi, "unencrypted accept LWS_CONNMODE_SERVER_LISTENER", accept_fd, accept_fd >= 0); if (accept_fd < 0) { if (LWS_ERRNO == LWS_EAGAIN || LWS_ERRNO == LWS_EWOULDBLOCK) { lwsl_debug("accept asks to try again\n"); break; } lwsl_warn("ERROR on accept: %s\n", strerror(LWS_ERRNO)); break; } lws_plat_set_socket_options(context, accept_fd); /* * look at who we connected to and give user code a chance * to reject based on client IP. There's no protocol selected * yet so we issue this to protocols[0] */ if ((context->protocols[0].callback)(context, wsi, LWS_CALLBACK_FILTER_NETWORK_CONNECTION, NULL, (void *)(long)accept_fd, 0)) { lwsl_debug("Callback denied network connection\n"); compatible_close(accept_fd); break; } new_wsi = libwebsocket_create_new_server_wsi(context); if (new_wsi == NULL) { compatible_close(accept_fd); break; } new_wsi->sock = accept_fd; /* the transport is accepted... give him time to negotiate */ libwebsocket_set_timeout(new_wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER, AWAITING_TIMEOUT); /* * A new connection was accepted. Give the user a chance to * set properties of the newly created wsi. There's no protocol * selected yet so we issue this to protocols[0] */ (context->protocols[0].callback)(context, new_wsi, LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED, NULL, NULL, 0); #ifdef LWS_USE_LIBEV if (LWS_LIBEV_ENABLED(context)) { new_wsi->w_read.context = context; new_wsi->w_write.context = context; /* new_wsi->w_read.wsi = new_wsi; new_wsi->w_write.wsi = new_wsi; */ struct ev_io* w_read = (struct ev_io*)&(new_wsi->w_read); struct ev_io* w_write = (struct ev_io*)&(new_wsi->w_write); ev_io_init(w_read,libwebsocket_accept_cb,accept_fd,EV_READ); ev_io_init(w_write,libwebsocket_accept_cb,accept_fd,EV_WRITE); } #endif /* LWS_USE_LIBEV */ #ifdef LWS_OPENSSL_SUPPORT new_wsi->ssl = NULL; if (!context->use_ssl) { #endif lwsl_debug("accepted new conn port %u on fd=%d\n", ntohs(cli_addr.sin_port), accept_fd); insert_wsi_socket_into_fds(context, new_wsi); break; #ifdef LWS_OPENSSL_SUPPORT } new_wsi->ssl = SSL_new(context->ssl_ctx); if (new_wsi->ssl == NULL) { lwsl_err("SSL_new failed: %s\n", ERR_error_string(SSL_get_error( new_wsi->ssl, 0), NULL)); libwebsockets_decode_ssl_error(); free(new_wsi); compatible_close(accept_fd); break; } SSL_set_ex_data(new_wsi->ssl, openssl_websocket_private_data_index, context); SSL_set_fd(new_wsi->ssl, accept_fd); #ifndef USE_CYASSL SSL_set_mode(new_wsi->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); #endif #ifdef USE_CYASSL CyaSSL_set_using_nonblock(new_wsi->ssl, 1); #else bio = SSL_get_rbio(new_wsi->ssl); if (bio) BIO_set_nbio(bio, 1); /* nonblocking */ else lwsl_notice("NULL rbio\n"); bio = SSL_get_wbio(new_wsi->ssl); if (bio) BIO_set_nbio(bio, 1); /* nonblocking */ else lwsl_notice("NULL rbio\n"); #endif /* * we are not accepted yet, but we need to enter ourselves * as a live connection. That way we can retry when more * pieces come if we're not sorted yet */ wsi = new_wsi; wsi->mode = LWS_CONNMODE_SSL_ACK_PENDING; insert_wsi_socket_into_fds(context, wsi); libwebsocket_set_timeout(wsi, PENDING_TIMEOUT_SSL_ACCEPT, AWAITING_TIMEOUT); lwsl_info("inserted SSL accept into fds, trying SSL_accept\n"); /* fallthru */ case LWS_CONNMODE_SSL_ACK_PENDING: if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) goto fail; #ifdef LWS_USE_LIBEV if (LWS_LIBEV_ENABLED(context)) ev_io_stop(context->io_loop, (struct ev_io *)&wsi->w_write); #endif /* LWS_USE_LIBEV */ lws_latency_pre(context, wsi); n = recv(wsi->sock, context->service_buffer, sizeof(context->service_buffer), MSG_PEEK); /* * optionally allow non-SSL connect on SSL listening socket * This is disabled by default, if enabled it goes around any * SSL-level access control (eg, client-side certs) so leave * it disabled unless you know it's not a problem for you */ if (context->allow_non_ssl_on_ssl_port && n >= 1 && context->service_buffer[0] >= ' ') { /* * TLS content-type for Handshake is 0x16 * TLS content-type for ChangeCipherSpec Record is 0x14 * * A non-ssl session will start with the HTTP method in * ASCII. If we see it's not a legit SSL handshake * kill the SSL for this connection and try to handle * as a HTTP connection upgrade directly. */ wsi->use_ssl = 0; SSL_shutdown(wsi->ssl); SSL_free(wsi->ssl); wsi->ssl = NULL; goto accepted; } /* normal SSL connection processing path */ n = SSL_accept(wsi->ssl); lws_latency(context, wsi, "SSL_accept LWS_CONNMODE_SSL_ACK_PENDING\n", n, n == 1); if (n != 1) { m = SSL_get_error(wsi->ssl, n); lwsl_debug("SSL_accept failed %d / %s\n", m, ERR_error_string(m, NULL)); if (m == SSL_ERROR_WANT_READ) { if (lws_change_pollfd(wsi, 0, LWS_POLLIN)) goto fail; #ifdef LWS_USE_LIBEV if (LWS_LIBEV_ENABLED(context)) ev_io_start(context->io_loop, (struct ev_io *)&wsi->w_read); #endif /* LWS_USE_LIBEV */ lwsl_info("SSL_ERROR_WANT_READ\n"); break; } if (m == SSL_ERROR_WANT_WRITE) { if (lws_change_pollfd(wsi, 0, LWS_POLLOUT)) goto fail; #ifdef LWS_USE_LIBEV if (LWS_LIBEV_ENABLED(context)) ev_io_start(context->io_loop, (struct ev_io *)&wsi->w_write); #endif /* LWS_USE_LIBEV */ break; } lwsl_debug("SSL_accept failed skt %u: %s\n", pollfd->fd, ERR_error_string(m, NULL)); libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); break; } accepted: /* OK, we are accepted... give him some time to negotiate */ libwebsocket_set_timeout(wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER, AWAITING_TIMEOUT); wsi->mode = LWS_CONNMODE_HTTP_SERVING; lwsl_debug("accepted new SSL conn\n"); break; #endif default: break; } return 0; fail: libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); return 1; }
int lws_handle_POLLOUT_event(struct libwebsocket_context *context, struct libwebsocket *wsi, struct libwebsocket_pollfd *pollfd) { int n; struct lws_tokens eff_buf; int ret; int m; int handled = 0; /* pending truncated sends have uber priority */ if (wsi->truncated_send_len) { lws_issue_raw(wsi, wsi->truncated_send_malloc + wsi->truncated_send_offset, wsi->truncated_send_len); /* leave POLLOUT active either way */ return 0; } m = lws_ext_callback_for_each_active(wsi, LWS_EXT_CALLBACK_IS_WRITEABLE, NULL, 0); if (handled == 1) goto notify_action; #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) 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)) return 1; #ifdef LWS_USE_LIBEV if (LWS_LIBEV_ENABLED(context)) ev_io_stop(context->io_loop, (struct ev_io *)&wsi->w_write); #endif /* LWS_USE_LIBEV */ } notify_action: if (wsi->mode == LWS_CONNMODE_WS_CLIENT) n = LWS_CALLBACK_CLIENT_WRITEABLE; else n = LWS_CALLBACK_SERVER_WRITEABLE; return user_callback_handle_rxflow(wsi->protocol->callback, context, wsi, (enum libwebsocket_callback_reasons) n, wsi->user_space, NULL, 0); }