void lws_libuv_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]; struct lws_io_watcher *w = &wsi->w_read; //#if defined(WIN32) || defined(_WIN32) // int current_events = w->uv_watcher.events & // (UV_READABLE | UV_WRITABLE); //#else int current_events = w->actual_events & (UV_READABLE | UV_WRITABLE); //#endif if (!LWS_LIBUV_ENABLED(context)) return; // w->context is set after the loop is initialized if (!pt->io_loop_uv || !w->context) { lwsl_info("%s: no io loop yet\n", __func__); return; } if (!((flags & (LWS_EV_START | LWS_EV_STOP)) && (flags & (LWS_EV_READ | LWS_EV_WRITE)))) { lwsl_err("%s: assert: flags %d", __func__, flags); assert(0); } if (flags & LWS_EV_START) { if (flags & LWS_EV_WRITE) current_events |= UV_WRITABLE; if (flags & LWS_EV_READ) current_events |= UV_READABLE; uv_poll_start(&w->uv_watcher, current_events, lws_io_cb); } else { if (flags & LWS_EV_WRITE) current_events &= ~UV_WRITABLE; if (flags & LWS_EV_READ) current_events &= ~UV_READABLE; if (!(current_events & (UV_READABLE | UV_WRITABLE))) uv_poll_stop(&w->uv_watcher); else uv_poll_start(&w->uv_watcher, current_events, lws_io_cb); } w->actual_events = current_events; }
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); }
static void elops_io_uv(struct lws *wsi, int flags) { struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; struct lws_io_watcher *w = &wsi->w_read; int current_events = w->actual_events & (UV_READABLE | UV_WRITABLE); lwsl_debug("%s: %p: %d\n", __func__, wsi, flags); /* w->context is set after the loop is initialized */ if (!pt->uv.io_loop || !w->context) { lwsl_info("%s: no io loop yet\n", __func__); return; } if (!((flags & (LWS_EV_START | LWS_EV_STOP)) && (flags & (LWS_EV_READ | LWS_EV_WRITE)))) { lwsl_err("%s: assert: flags %d", __func__, flags); assert(0); } if (!w->uv.pwatcher || wsi->told_event_loop_closed) { lwsl_err("%s: no watcher\n", __func__); return; } if (flags & LWS_EV_START) { if (flags & LWS_EV_WRITE) current_events |= UV_WRITABLE; if (flags & LWS_EV_READ) current_events |= UV_READABLE; uv_poll_start(w->uv.pwatcher, current_events, lws_io_cb); } else { if (flags & LWS_EV_WRITE) current_events &= ~UV_WRITABLE; if (flags & LWS_EV_READ) current_events &= ~UV_READABLE; if (!(current_events & (UV_READABLE | UV_WRITABLE))) uv_poll_stop(w->uv.pwatcher); else uv_poll_start(w->uv.pwatcher, current_events, lws_io_cb); } w->actual_events = current_events; }
void lws_libuv_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 defined(WIN32) || defined(_WIN32) int current_events = wsi->w_read.uv_watcher.events & (UV_READABLE | UV_WRITABLE); #else int current_events = wsi->w_read.uv_watcher.io_watcher.pevents & (UV_READABLE | UV_WRITABLE); #endif struct lws_io_watcher *w = &wsi->w_read; if (!LWS_LIBUV_ENABLED(context)) return; // lwsl_notice("%s: wsi: %p, flags:0x%x\n", __func__, wsi, flags); if (!pt->io_loop_uv) { lwsl_info("%s: no io loop yet\n", __func__); return; } if (!((flags & (LWS_EV_START | LWS_EV_STOP)) && (flags & (LWS_EV_READ | LWS_EV_WRITE)))) { lwsl_err("%s: assert: flags %d", __func__, flags); assert(0); } if (flags & LWS_EV_START) { if (flags & LWS_EV_WRITE) current_events |= UV_WRITABLE; if (flags & LWS_EV_READ) current_events |= UV_READABLE; uv_poll_start(&w->uv_watcher, current_events, lws_io_cb); } else { if (flags & LWS_EV_WRITE) current_events &= ~UV_WRITABLE; if (flags & LWS_EV_READ) current_events &= ~UV_READABLE; if (!(current_events & (UV_READABLE | UV_WRITABLE))) uv_poll_stop(&w->uv_watcher); else uv_poll_start(&w->uv_watcher, current_events, lws_io_cb); } }
int LWS_WARN_UNUSED_RESULT lws_http_transaction_completed_client(struct lws *wsi) { lwsl_debug("%s: wsi %p\n", __func__, wsi); /* if we can't go back to accept new headers, drop the connection */ if (wsi->u.http.connection_type != HTTP_CONNECTION_KEEP_ALIVE) { lwsl_info("%s: %p: close connection\n", __func__, wsi); return 1; } /* we don't support chained client connections yet */ return 1; #if 0 /* otherwise set ourselves up ready to go again */ wsi->state = LWSS_CLIENT_HTTP_ESTABLISHED; wsi->mode = LWSCM_HTTP_CLIENT_ACCEPTED; wsi->u.http.content_length = 0; wsi->hdr_parsing_completed = 0; /* He asked for it to stay alive indefinitely */ lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0); /* * As client, nothing new is going to come until we ask for it * we can drop the ah, if any */ if (wsi->u.hdr.ah) { lws_header_table_force_to_detachable_state(wsi); lws_header_table_detach(wsi, 0); } /* If we're (re)starting on headers, need other implied init */ wsi->u.hdr.ues = URIES_IDLE; lwsl_info("%s: %p: keep-alive await new transaction\n", __func__, wsi); return 0; #endif }
int libwebsocket_ensure_user_space(struct libwebsocket *wsi) { lwsl_info("%s: %p protocol %p\n", __func__, wsi, wsi->protocol); if (!wsi->protocol) return 1; /* allocate the per-connection user memory (if any) */ if (wsi->protocol->per_session_data_size && !wsi->user_space) { wsi->user_space = malloc( wsi->protocol->per_session_data_size); if (wsi->user_space == NULL) { lwsl_err("Out of memory for conn user space\n"); return 1; } memset(wsi->user_space, 0, wsi->protocol->per_session_data_size); } else lwsl_info("%s: %p protocol pss %u, user_space=%d\n", __func__, wsi, wsi->protocol->per_session_data_size, wsi->user_space); return 0; }
static int lws_gate_accepts(struct lws_context *context, int on) { struct lws_vhost *v = context->vhost_list; lwsl_info("gating accepts %d\n", on); context->ssl_gate_accepts = !on; #if defined(LWS_WITH_STATS) context->updated = 1; #endif while (v) { if (v->use_ssl && v->lserv_wsi) /* gate ability to accept incoming connections */ if (lws_change_pollfd(v->lserv_wsi, (LWS_POLLIN) * !on, (LWS_POLLIN) * on)) lwsl_info("Unable to set accept POLLIN %d\n", on); v = v->vhost_next; } return 0; }
static struct lws_urldecode_stateful * lws_urldecode_s_create(struct lws *wsi, char *out, int out_len, void *data, lws_urldecode_stateful_cb output) { struct lws_urldecode_stateful *s = lws_zalloc(sizeof(*s), "stateful urldecode"); char buf[200], *p; int m = 0; if (!s) return NULL; s->out = out; s->out_len = out_len; s->output = output; s->pos = 0; s->sum = 0; s->mp = 0; s->state = US_NAME; s->name[0] = '\0'; s->data = data; s->wsi = wsi; if (lws_hdr_copy(wsi, buf, sizeof(buf), WSI_TOKEN_HTTP_CONTENT_TYPE) > 0) { /* multipart/form-data; boundary=----WebKitFormBoundarycc7YgAPEIHvgE9Bf */ if (!strncmp(buf, "multipart/form-data", 19)) { s->multipart_form_data = 1; s->state = MT_LOOK_BOUND_IN; s->mp = 2; p = strstr(buf, "boundary="); if (p) { p += 9; s->mime_boundary[m++] = '\x0d'; s->mime_boundary[m++] = '\x0a'; s->mime_boundary[m++] = '-'; s->mime_boundary[m++] = '-'; while (m < (int)sizeof(s->mime_boundary) - 1 && *p && *p != ' ') s->mime_boundary[m++] = *p++; s->mime_boundary[m] = '\0'; lwsl_info("boundary '%s'\n", s->mime_boundary); } } } return s; }
static void lws_uv_close_cb_sa(uv_handle_t *handle) { struct lws_context *context = LWS_UV_REFCOUNT_STATIC_HANDLE_TO_CONTEXT(handle); int n; lwsl_info("%s: sa left %d: dyn left: %d\n", __func__, context->count_event_loop_static_asset_handles, context->count_wsi_allocated); /* any static assets left? */ if (LWS_UV_REFCOUNT_STATIC_HANDLE_DESTROYED(handle) || context->count_wsi_allocated) return; /* * That's it... all wsi were down, and now every * static asset lws had a UV handle for is down. * * Stop the loop so we can get out of here. */ for (n = 0; n < context->count_threads; n++) { struct lws_context_per_thread *pt = &context->pt[n]; if (pt->uv.io_loop && !pt->event_loop_foreign) uv_stop(pt->uv.io_loop); } if (!context->pt[0].event_loop_foreign) { lwsl_info("%s: calling lws_context_destroy2\n", __func__); lws_context_destroy2(context); } lwsl_info("%s: all done\n", __func__); }
static int ws_def_callback(struct libwebsocket_context *context, struct libwebsocket *wsi, enum libwebsocket_callback_reasons reason, void *user, void *in, size_t len) { switch (reason) { case LWS_CALLBACK_CLIENT_ESTABLISHED: lwsl_info("dumb: LWS_CALLBACK_CLIENT_ESTABLISHED\n"); break; case LWS_CALLBACK_CLOSED: lwsl_notice("dumb: LWS_CALLBACK_CLOSED\n"); wsi_cl = NULL;force_exit=1; break; case LWS_CALLBACK_CLIENT_RECEIVE: ((char *)in)[len] = '\0'; lwsl_info("rx %d '%s'\n", (int)len, (char *)in); break; /* because we are protocols[0] ... */ case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: if (wsi == wsi_cl) { lwsl_err("dumb: LWS_CALLBACK_CLIENT_CONNECTION_ERROR\n"); wsi_cl = NULL; } break; default: break; } return 0; }
void lws_ssl_elaborate_error(void) { #if defined(LWS_WITH_MBEDTLS) #else char buf[256]; u_long err; while ((err = ERR_get_error()) != 0) { ERR_error_string_n(err, buf, sizeof(buf)); lwsl_info("*** %s\n", buf); } #endif }
static int esp8266_find_free_conn(struct lws_context *context) { int n; for (n = 0; n < context->max_fds; n++) if (!context->connpool[n]) { lwsl_info(" using connpool %d\n", n); return n; } lwsl_err("%s: no free conns\n", __func__); return -1; }
int _libwebsocket_rx_flow_control(struct libwebsocket *wsi) { struct libwebsocket_context *context = wsi->protocol->owning_server; /* 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 (wsi->rxflow_buffer) { /* get ourselves called back to deal with stashed buffer */ libwebsocket_callback_on_writable(context, wsi); return 0; } /* 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) { 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; }
/* * This does not actually stop the event loop. The reason is we have to pass * libuv handle closures through its event loop. So this tries to close all * wsi, and set a flag; when all the wsi closures are finalized then we * actually stop the libuv event loops. */ static void lws_libuv_stop(struct lws_context *context) { struct lws_context_per_thread *pt; int n, m; lwsl_err("%s\n", __func__); if (context->requested_kill) { lwsl_err("%s: ignoring\n", __func__); return; } context->requested_kill = 1; m = context->count_threads; context->being_destroyed = 1; /* * Phase 1: start the close of every dynamic uv handle */ while (m--) { pt = &context->pt[m]; if (pt->pipe_wsi) { uv_poll_stop(pt->pipe_wsi->w_read.uv.pwatcher); lws_destroy_event_pipe(pt->pipe_wsi); pt->pipe_wsi = NULL; } for (n = 0; (unsigned int)n < context->pt[m].fds_count; n++) { struct lws *wsi = wsi_from_fd(context, pt->fds[n].fd); if (!wsi) continue; lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY, __func__ /* no protocol close */); n--; } } lwsl_info("%s: started closing all wsi\n", __func__); /* we cannot have completed... there are at least the cancel pipes */ }
struct lws * lws_create_server_child_wsi(struct lws_vhost *vhost, struct lws *parent_wsi, unsigned int sid) { struct lws *wsi = lws_create_new_server_wsi(vhost); if (!wsi) return NULL; /* no more children allowed by parent */ if (parent_wsi->u.http2.child_count + 1 == parent_wsi->u.http2.peer_settings.setting[ LWS_HTTP2_SETTINGS__MAX_CONCURRENT_STREAMS]) goto bail; lws_http2_init(&wsi->u.http2.peer_settings); lws_http2_init(&wsi->u.http2.my_settings); wsi->u.http2.stream_id = sid; wsi->u.http2.my_stream_id = sid; wsi->u.http2.parent_wsi = parent_wsi; wsi->u.http2.next_child_wsi = parent_wsi->u.http2.next_child_wsi; parent_wsi->u.http2.next_child_wsi = wsi; parent_wsi->u.http2.child_count++; wsi->u.http2.my_priority = 16; wsi->u.http2.tx_credit = 65535; wsi->state = LWSS_HTTP2_ESTABLISHED; wsi->mode = parent_wsi->mode; wsi->protocol = &vhost->protocols[0]; if (lws_ensure_user_space(wsi)) goto bail; lwsl_info("%s: %p new child %p, sid %d, user_space=%p\n", __func__, parent_wsi, wsi, sid, wsi->user_space); return wsi; bail: vhost->protocols[0].callback(wsi, LWS_CALLBACK_WSI_DESTROY, NULL, NULL, 0); lws_free(wsi); return NULL; }
int insert_wsi_socket_into_fds(struct libwebsocket_context *context, struct libwebsocket *wsi) { struct libwebsocket_pollargs pa = { wsi->sock, LWS_POLLIN, 0 }; if (context->fds_count >= context->max_fds) { lwsl_err("Too many fds (%d)\n", context->max_fds); return 1; } if (wsi->sock >= context->max_fds) { lwsl_err("Socket fd %d is too high (%d)\n", wsi->sock, context->max_fds); return 1; } assert(wsi); assert(wsi->sock >= 0); lwsl_info("insert_wsi_socket_into_fds: wsi=%p, sock=%d, fds pos=%d\n", wsi, wsi->sock, context->fds_count); context->protocols[0].callback(context, wsi, LWS_CALLBACK_LOCK_POLL, wsi->user_space, (void *) &pa, 0); context->lws_lookup[wsi->sock] = wsi; wsi->position_in_fds_table = context->fds_count; context->fds[context->fds_count].fd = wsi->sock; context->fds[context->fds_count].events = LWS_POLLIN; lws_plat_insert_socket_into_fds(context, wsi); /* external POLL support via protocol 0 */ context->protocols[0].callback(context, wsi, LWS_CALLBACK_ADD_POLL_FD, wsi->user_space, (void *) &pa, 0); context->protocols[0].callback(context, wsi, LWS_CALLBACK_UNLOCK_POLL, wsi->user_space, (void *)&pa, 0); return 0; }
static int lws_ssl_server_name_cb(SSL *ssl, int *ad, void *arg) { struct lws_context *context; struct lws_vhost *vhost, *vh; const char *servername; int port; if (!ssl) return SSL_TLSEXT_ERR_NOACK; context = (struct lws_context *)SSL_CTX_get_ex_data( SSL_get_SSL_CTX(ssl), openssl_SSL_CTX_private_data_index); /* * We can only get ssl accepted connections by using a vhost's ssl_ctx * find out which listening one took us and only match vhosts on the * same port. */ vh = context->vhost_list; while (vh) { if (vh->ssl_ctx == SSL_get_SSL_CTX(ssl)) break; vh = vh->vhost_next; } assert(vh); /* we cannot get an ssl without using a vhost ssl_ctx */ port = vh->listen_port; servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); if (servername) { vhost = lws_select_vhost(context, port, servername); if (vhost) { lwsl_info("SNI: Found: %s\n", servername); SSL_set_SSL_CTX(ssl, vhost->ssl_ctx); return SSL_TLSEXT_ERR_OK; } lwsl_err("SNI: Unknown ServerName: %s\n", servername); } return SSL_TLSEXT_ERR_OK; }
void lws_libuv_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]; int current_events = wsi->w_read.uv_watcher.io_watcher.pevents & (UV_READABLE | UV_WRITABLE); struct lws_io_watcher *w = &wsi->w_read; if (!LWS_LIBUV_ENABLED(context)) return; lwsl_debug("%s: wsi: %p, flags:%d\n", __func__, wsi, flags); if (!pt->io_loop_uv) { lwsl_info("%s: no io loop yet\n", __func__); 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) current_events |= UV_WRITABLE; if (flags & LWS_EV_READ) current_events |= UV_READABLE; uv_poll_start(&w->uv_watcher, current_events, lws_accept_cb); } else { if (flags & LWS_EV_WRITE) current_events &= ~UV_WRITABLE; if (flags & LWS_EV_READ) current_events &= ~UV_READABLE; if (!(current_events & (UV_READABLE | UV_WRITABLE))) uv_poll_stop(&w->uv_watcher); else uv_poll_start(&w->uv_watcher, current_events, lws_accept_cb); } }
int lws_tls_client_confirm_peer_cert(struct lws *wsi, char *ebuf, int ebuf_len) { int n; X509 *peer = SSL_get_peer_certificate(wsi->tls.ssl); struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; char *sb = (char *)&pt->serv_buf[0]; if (!peer) { lwsl_info("peer did not provide cert\n"); return -1; } lwsl_info("peer provided cert\n"); n = SSL_get_verify_result(wsi->tls.ssl); lws_latency(wsi->context, wsi, "SSL_get_verify_result LWS_CONNMODE..HANDSHAKE", n, n > 0); lwsl_debug("get_verify says %d\n", n); if (n == X509_V_OK) return 0; if (n == X509_V_ERR_HOSTNAME_MISMATCH && (wsi->tls.use_ssl & LCCSCF_SKIP_SERVER_CERT_HOSTNAME_CHECK)) { lwsl_info("accepting certificate for invalid hostname\n"); return 0; } if (n == X509_V_ERR_INVALID_CA && (wsi->tls.use_ssl & LCCSCF_ALLOW_SELFSIGNED)) { lwsl_info("accepting certificate from untrusted CA\n"); return 0; } if ((n == X509_V_ERR_CERT_NOT_YET_VALID || n == X509_V_ERR_CERT_HAS_EXPIRED) && (wsi->tls.use_ssl & LCCSCF_ALLOW_EXPIRED)) { lwsl_info("accepting expired or not yet valid certificate\n"); return 0; } lws_snprintf(ebuf, ebuf_len, "server's cert didn't look good, X509_V_ERR = %d: %s\n", n, ERR_error_string(n, sb)); lwsl_info("%s\n", ebuf); lws_ssl_elaborate_error(); return -1; }
int lws_add_http_header_content_length(struct lws *wsi, lws_filepos_t content_length, unsigned char **p, unsigned char *end) { char b[24]; int n; n = sprintf(b, "%llu", (unsigned long long)content_length); if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH, (unsigned char *)b, n, p, end)) return 1; wsi->http.tx_content_length = content_length; wsi->http.tx_content_remain = content_length; lwsl_info("%s: wsi %p: tx_content_length/remain %llu\n", __func__, wsi, (unsigned long long)content_length); return 0; }
static void elops_destroy_pt_uv(struct lws_context *context, int tsi) { struct lws_context_per_thread *pt = &context->pt[tsi]; int m, ns; lwsl_info("%s: %d\n", __func__, tsi); if (!lws_check_opt(context->options, LWS_SERVER_OPTION_LIBUV)) return; if (!pt->uv.io_loop) return; if (pt->event_loop_destroy_processing_done) return; pt->event_loop_destroy_processing_done = 1; if (!pt->event_loop_foreign) { uv_signal_stop(&pt->w_sigint.uv.watcher); ns = LWS_ARRAY_SIZE(sigs); if (lws_check_opt(context->options, LWS_SERVER_OPTION_UV_NO_SIGSEGV_SIGFPE_SPIN)) ns = 2; for (m = 0; m < ns; m++) { uv_signal_stop(&pt->uv.signals[m]); uv_close((uv_handle_t *)&pt->uv.signals[m], lws_uv_close_cb_sa); } } else lwsl_debug("%s: not closing pt signals\n", __func__); uv_timer_stop(&pt->uv.timeout_watcher); uv_close((uv_handle_t *)&pt->uv.timeout_watcher, lws_uv_close_cb_sa); uv_timer_stop(&pt->uv.hrtimer); uv_close((uv_handle_t *)&pt->uv.hrtimer, lws_uv_close_cb_sa); uv_idle_stop(&pt->uv.idle); uv_close((uv_handle_t *)&pt->uv.idle, lws_uv_close_cb_sa); }
int lws_context_init_ssl_library(struct lws_context_creation_info *info) { #ifdef USE_WOLFSSL #ifdef USE_OLD_CYASSL lwsl_info(" Compiled with CyaSSL support\n"); #else lwsl_info(" Compiled with wolfSSL support\n"); #endif #else #if defined(LWS_WITH_BORINGSSL) lwsl_info(" Compiled with BoringSSL support\n"); #else #if defined(LWS_WITH_MBEDTLS) lwsl_info(" Compiled with MbedTLS support\n"); #else lwsl_info(" Compiled with OpenSSL support\n"); #endif #endif #endif if (!lws_check_opt(info->options, LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT)) { lwsl_info(" SSL disabled: no LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT\n"); return 0; } /* basic openssl init */ lwsl_info("Doing SSL library init\n"); #if !defined(LWS_WITH_MBEDTLS) SSL_library_init(); OpenSSL_add_all_algorithms(); SSL_load_error_strings(); openssl_websocket_private_data_index = SSL_get_ex_new_index(0, "lws", NULL, NULL, NULL); openssl_SSL_CTX_private_data_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL); #endif return 0; }
static void esp8266_cb_disconnected(void *arg) { struct espconn *conn = arg; struct lws *wsi = conn->reverse; int n; lwsl_notice("%s: %p\n", __func__, wsi); for (n = 0; n < hacky_context->max_fds; n++) if (hacky_context->connpool[n] == arg) { hacky_context->connpool[n] = NULL; lwsl_info(" freed connpool %d\n", n); } if (wsi) { conn->reverse = NULL; lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); lwsl_notice("closed ok\n"); } }
static int lws_calllback_as_writeable(struct libwebsocket_context *context, struct libwebsocket *wsi) { int n; switch (wsi->mode) { case LWS_CONNMODE_WS_CLIENT: n = LWS_CALLBACK_CLIENT_WRITEABLE; break; case LWS_CONNMODE_WS_SERVING: n = LWS_CALLBACK_SERVER_WRITEABLE; break; default: n = LWS_CALLBACK_HTTP_WRITEABLE; break; } lwsl_info("%s: %p (user=%p)\n", __func__, wsi, wsi->user_space); return user_callback_handle_rxflow(wsi->protocol->callback, context, wsi, (enum libwebsocket_callback_reasons) n, wsi->user_space, NULL, 0); }
struct lws * lws_create_server_child_wsi(struct lws_context *context, struct lws *parent_wsi, unsigned int sid) { struct lws *wsi = lws_create_new_server_wsi(context); if (!wsi) return NULL; /* no more children allowed by parent */ if (parent_wsi->u.http2.child_count + 1 == parent_wsi->u.http2.peer_settings.setting[ LWS_HTTP2_SETTINGS__MAX_CONCURRENT_STREAMS]) return NULL; lws_http2_init(&wsi->u.http2.peer_settings); lws_http2_init(&wsi->u.http2.my_settings); wsi->u.http2.stream_id = sid; wsi->u.http2.my_stream_id = sid; wsi->u.http2.parent_wsi = parent_wsi; wsi->u.http2.next_child_wsi = parent_wsi->u.http2.next_child_wsi; parent_wsi->u.http2.next_child_wsi = wsi; parent_wsi->u.http2.child_count++; wsi->u.http2.my_priority = 16; wsi->u.http2.tx_credit = 65535; wsi->state = WSI_STATE_HTTP2_ESTABLISHED; wsi->mode = parent_wsi->mode; wsi->protocol = &context->protocols[0]; lws_ensure_user_space(wsi); lwsl_info("%s: %p new child %p, sid %d, user_space=%p\n", __func__, parent_wsi, wsi, sid, wsi->user_space); return wsi; }
void lws_context_init_alpn(struct lws_vhost *vhost) { #if defined(LWS_WITH_MBEDTLS) || (defined(OPENSSL_VERSION_NUMBER) && \ OPENSSL_VERSION_NUMBER >= 0x10002000L) const char *alpn_comma = vhost->context->tls.alpn_default; if (vhost->tls.alpn) alpn_comma = vhost->tls.alpn; lwsl_info(" Server '%s' advertising ALPN: %s\n", vhost->name, alpn_comma); vhost->tls.alpn_ctx.len = lws_alpn_comma_to_openssl(alpn_comma, vhost->tls.alpn_ctx.data, sizeof(vhost->tls.alpn_ctx.data) - 1); SSL_CTX_set_alpn_select_cb(vhost->tls.ssl_ctx, alpn_cb, &vhost->tls.alpn_ctx); #else lwsl_err( " HTTP2 / ALPN configured but not supported by OpenSSL 0x%lx\n", OPENSSL_VERSION_NUMBER); #endif // OPENSSL_VERSION_NUMBER >= 0x10002000L }
static int stream_close(struct lws *wsi) { char buf[LWS_PRE + 6], *out = buf + LWS_PRE; if (wsi->http.did_stream_close) return 0; wsi->http.did_stream_close = 1; if (wsi->http2_substream) { if (lws_write(wsi, (unsigned char *)buf + LWS_PRE, 0, LWS_WRITE_HTTP_FINAL) < 0) { lwsl_info("%s: COMPL_CLIENT_HTTP: h2 fin wr failed\n", __func__); return -1; } } else { *out++ = '0'; *out++ = '\x0d'; *out++ = '\x0a'; *out++ = '\x0d'; *out++ = '\x0a'; if (lws_write(wsi, (unsigned char *)buf + LWS_PRE, 5, LWS_WRITE_HTTP_FINAL) < 0) { lwsl_err("%s: COMPL_CLIENT_HTTP: " "h2 final write failed\n", __func__); return -1; } } return 0; }
int lws_http2_frame_write(struct lws *wsi, int type, int flags, unsigned int sid, unsigned int len, unsigned char *buf) { struct lws *wsi_eff = lws_http2_get_network_wsi(wsi); unsigned char *p = &buf[-LWS_HTTP2_FRAME_HEADER_LENGTH]; int n; *p++ = len >> 16; *p++ = len >> 8; *p++ = len; *p++ = type; *p++ = flags; *p++ = sid >> 24; *p++ = sid >> 16; *p++ = sid >> 8; *p++ = sid; lwsl_info("%s: %p (eff %p). type %d, flags 0x%x, sid=%d, len=%d\n", __func__, wsi, wsi_eff, type, flags, sid, len, wsi->u.http2.tx_credit); if (type == LWS_HTTP2_FRAME_TYPE_DATA) { if (wsi->u.http2.tx_credit < len) lwsl_err("%s: %p: sending payload len %d" " but tx_credit only %d!\n", len, wsi->u.http2.tx_credit); wsi->u.http2.tx_credit -= len; } n = lws_issue_raw(wsi_eff, &buf[-LWS_HTTP2_FRAME_HEADER_LENGTH], len + LWS_HTTP2_FRAME_HEADER_LENGTH); if (n >= LWS_HTTP2_FRAME_HEADER_LENGTH) return n - LWS_HTTP2_FRAME_HEADER_LENGTH; return n; }
static int lwsws_get_config(void *user, const char *f, const char * const *paths, int count_paths, lejp_callback cb) { unsigned char buf[128]; struct lejp_ctx ctx; int n, m, fd; fd = open(f, O_RDONLY); if (fd < 0) { lwsl_err("Cannot open %s\n", f); return 2; } lwsl_info("%s: %s\n", __func__, f); lejp_construct(&ctx, cb, user, paths, count_paths); do { n = read(fd, buf, sizeof(buf)); if (!n) break; m = (int)(signed char)lejp_parse(&ctx, buf, n); } while (m == LEJP_CONTINUE); close(fd); n = ctx.line; lejp_destruct(&ctx); if (m < 0) { lwsl_err("%s(%u): parsing error %d: %s\n", f, n, m, parser_errs[-m]); return 2; } return 0; }
int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { struct per_session_data__http *pss = (struct per_session_data__http *)user; unsigned char buffer[4096 + LWS_PRE]; unsigned long amount, file_len, sent; char leaf_path[1024]; const char *mimetype; char *other_headers; unsigned char *end; struct timeval tv; unsigned char *p; char buf[256]; char b64[64]; int n, m; #ifdef EXTERNAL_POLL struct lws_pollargs *pa = (struct lws_pollargs *)in; #endif switch (reason) { case LWS_CALLBACK_HTTP: if (debug_level & LLL_INFO) { dump_handshake_info(wsi); /* dump the individual URI Arg parameters */ n = 0; while (lws_hdr_copy_fragment(wsi, buf, sizeof(buf), WSI_TOKEN_HTTP_URI_ARGS, n) > 0) { lwsl_notice("URI Arg %d: %s\n", ++n, buf); } } { char name[100], rip[50]; lws_get_peer_addresses(wsi, lws_get_socket_fd(wsi), name, sizeof(name), rip, sizeof(rip)); sprintf(buf, "%s (%s)", name, rip); lwsl_notice("HTTP connect from %s\n", buf); } if (len < 1) { lws_return_http_status(wsi, HTTP_STATUS_BAD_REQUEST, NULL); goto try_to_reuse; } /* this example server has no concept of directories */ if (strchr((const char *)in + 1, '/')) { lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL); goto try_to_reuse; } /* if a legal POST URL, let it continue and accept data */ if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)) return 0; /* check for the "send a big file by hand" example case */ if (!strcmp((const char *)in, "/leaf.jpg")) { if (strlen(resource_path) > sizeof(leaf_path) - 10) return -1; sprintf(leaf_path, "%s/leaf.jpg", resource_path); /* well, let's demonstrate how to send the hard way */ p = buffer + LWS_PRE; end = p + sizeof(buffer) - LWS_PRE; pss->fd = lws_plat_file_open(wsi, leaf_path, &file_len, LWS_O_RDONLY); if (pss->fd == LWS_INVALID_FILE) { lwsl_err("faild to open file %s\n", leaf_path); return -1; } /* * we will send a big jpeg file, but it could be * anything. Set the Content-Type: appropriately * so the browser knows what to do with it. * * Notice we use the APIs to build the header, which * will do the right thing for HTTP 1/1.1 and HTTP2 * depending on what connection it happens to be working * on */ if (lws_add_http_header_status(wsi, 200, &p, end)) return 1; if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_SERVER, (unsigned char *)"libwebsockets", 13, &p, end)) return 1; if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, (unsigned char *)"image/jpeg", 10, &p, end)) return 1; if (lws_add_http_header_content_length(wsi, file_len, &p, end)) return 1; if (lws_finalize_http_header(wsi, &p, end)) return 1; /* * send the http headers... * this won't block since it's the first payload sent * on the connection since it was established * (too small for partial) * * Notice they are sent using LWS_WRITE_HTTP_HEADERS * which also means you can't send body too in one step, * this is mandated by changes in HTTP2 */ *p = '\0'; lwsl_info("%s\n", buffer + LWS_PRE); n = lws_write(wsi, buffer + LWS_PRE, p - (buffer + LWS_PRE), LWS_WRITE_HTTP_HEADERS); if (n < 0) { lws_plat_file_close(wsi, pss->fd); return -1; } /* * book us a LWS_CALLBACK_HTTP_WRITEABLE callback */ lws_callback_on_writable(wsi); break; } /* if not, send a file the easy way */ strcpy(buf, resource_path); if (strcmp(in, "/")) { if (*((const char *)in) != '/') strcat(buf, "/"); strncat(buf, in, sizeof(buf) - strlen(resource_path)); } else /* default file to serve */ strcat(buf, "/test.html"); buf[sizeof(buf) - 1] = '\0'; /* refuse to serve files we don't understand */ mimetype = get_mimetype(buf); if (!mimetype) { lwsl_err("Unknown mimetype for %s\n", buf); lws_return_http_status(wsi, HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, NULL); return -1; } /* demonstrates how to set a cookie on / */ other_headers = leaf_path; p = (unsigned char *)leaf_path; if (!strcmp((const char *)in, "/") && !lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) { /* this isn't very unguessable but it'll do for us */ gettimeofday(&tv, NULL); n = sprintf(b64, "test=LWS_%u_%u_COOKIE;Max-Age=360000", (unsigned int)tv.tv_sec, (unsigned int)tv.tv_usec); if (lws_add_http_header_by_name(wsi, (unsigned char *)"set-cookie:", (unsigned char *)b64, n, &p, (unsigned char *)leaf_path + sizeof(leaf_path))) return 1; } if (lws_is_ssl(wsi) && lws_add_http_header_by_name(wsi, (unsigned char *) "Strict-Transport-Security:", (unsigned char *) "max-age=15768000 ; " "includeSubDomains", 36, &p, (unsigned char *)leaf_path + sizeof(leaf_path))) return 1; n = (char *)p - leaf_path; n = lws_serve_http_file(wsi, buf, mimetype, other_headers, n); if (n < 0 || ((n > 0) && lws_http_transaction_completed(wsi))) return -1; /* error or can't reuse connection: close the socket */ /* * notice that the sending of the file completes asynchronously, * we'll get a LWS_CALLBACK_HTTP_FILE_COMPLETION callback when * it's done */ break; case LWS_CALLBACK_HTTP_BODY: strncpy(buf, in, 20); buf[20] = '\0'; if (len < 20) buf[len] = '\0'; lwsl_notice("LWS_CALLBACK_HTTP_BODY: %s... len %d\n", (const char *)buf, (int)len); break; case LWS_CALLBACK_HTTP_BODY_COMPLETION: lwsl_notice("LWS_CALLBACK_HTTP_BODY_COMPLETION\n"); /* the whole of the sent body arrived, close or reuse the connection */ lws_return_http_status(wsi, HTTP_STATUS_OK, NULL); goto try_to_reuse; case LWS_CALLBACK_HTTP_FILE_COMPLETION: goto try_to_reuse; case LWS_CALLBACK_HTTP_WRITEABLE: lwsl_info("LWS_CALLBACK_HTTP_WRITEABLE\n"); if (pss->fd == LWS_INVALID_FILE) goto try_to_reuse; /* * we can send more of whatever it is we were sending */ sent = 0; do { /* we'd like the send this much */ n = sizeof(buffer) - LWS_PRE; /* but if the peer told us he wants less, we can adapt */ m = lws_get_peer_write_allowance(wsi); /* -1 means not using a protocol that has this info */ if (m == 0) /* right now, peer can't handle anything */ goto later; if (m != -1 && m < n) /* he couldn't handle that much */ n = m; n = lws_plat_file_read(wsi, pss->fd, &amount, buffer + LWS_PRE, n); /* problem reading, close conn */ if (n < 0) { lwsl_err("problem reading file\n"); goto bail; } n = (int)amount; /* sent it all, close conn */ if (n == 0) goto penultimate; /* * To support HTTP2, must take care about preamble space * * identification of when we send the last payload frame * is handled by the library itself if you sent a * content-length header */ m = lws_write(wsi, buffer + LWS_PRE, n, LWS_WRITE_HTTP); if (m < 0) { lwsl_err("write failed\n"); /* write failed, close conn */ goto bail; } if (m) /* while still active, extend timeout */ lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT, 5); sent += m; } while (!lws_send_pipe_choked(wsi) && (sent < 1024 * 1024)); later: lws_callback_on_writable(wsi); break; penultimate: lws_plat_file_close(wsi, pss->fd); pss->fd = LWS_INVALID_FILE; goto try_to_reuse; bail: lws_plat_file_close(wsi, pss->fd); return -1; /* * callback for confirming to continue with client IP appear in * protocol 0 callback since no websocket protocol has been agreed * yet. You can just ignore this if you won't filter on client IP * since the default uhandled callback return is 0 meaning let the * connection continue. */ case LWS_CALLBACK_FILTER_NETWORK_CONNECTION: /* if we returned non-zero from here, we kill the connection */ break; /* * callbacks for managing the external poll() array appear in * protocol 0 callback */ case LWS_CALLBACK_LOCK_POLL: /* * lock mutex to protect pollfd state * called before any other POLL related callback * if protecting wsi lifecycle change, len == 1 */ test_server_lock(len); break; case LWS_CALLBACK_UNLOCK_POLL: /* * unlock mutex to protect pollfd state when * called after any other POLL related callback * if protecting wsi lifecycle change, len == 1 */ test_server_unlock(len); break; #ifdef EXTERNAL_POLL case LWS_CALLBACK_ADD_POLL_FD: if (count_pollfds >= max_poll_elements) { lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n"); return 1; } fd_lookup[pa->fd] = count_pollfds; pollfds[count_pollfds].fd = pa->fd; pollfds[count_pollfds].events = pa->events; pollfds[count_pollfds++].revents = 0; break; case LWS_CALLBACK_DEL_POLL_FD: if (!--count_pollfds) break; m = fd_lookup[pa->fd]; /* have the last guy take up the vacant slot */ pollfds[m] = pollfds[count_pollfds]; fd_lookup[pollfds[count_pollfds].fd] = m; break; case LWS_CALLBACK_CHANGE_MODE_POLL_FD: pollfds[fd_lookup[pa->fd]].events = pa->events; break; #endif case LWS_CALLBACK_GET_THREAD_ID: /* * if you will call "lws_callback_on_writable" * from a different thread, return the caller thread ID * here so lws can use this information to work out if it * should signal the poll() loop to exit and restart early */ /* return pthread_getthreadid_np(); */ break; default: break; } return 0; /* if we're on HTTP1.1 or 2.0, will keep the idle connection alive */ try_to_reuse: if (lws_http_transaction_completed(wsi)) return -1; return 0; }