diff --git a/CMakeLists.txt b/CMakeLists.txt index 1eb909cc354d7a63a40ead14d2678f211eb374b0..9c6844246067ef662f9475b4699d881bab63c95e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -727,15 +727,15 @@ set(HDR_PUBLIC ) set(SOURCES - lib/misc/base64-decode.c + lib/core/alloc.c + lib/core/context.c lib/core/libwebsockets.c - lib/core/service.c - lib/core/pollfd.c lib/core/output.c - lib/core/context.c - lib/core/alloc.c - lib/roles/pipe/ops-pipe.c - lib/misc/lws-ring.c) + lib/core/pollfd.c + lib/core/service.c + lib/misc/base64-decode.c + lib/misc/lws-ring.c + lib/roles/pipe/ops-pipe.c) if (LWS_ROLE_H1 OR LWS_ROLE_H2) list(APPEND SOURCES @@ -764,7 +764,8 @@ endif() if (LWS_ROLE_RAW) list(APPEND SOURCES - lib/roles/raw/ops-raw.c) + lib/roles/raw-skt/ops-raw-skt.c + lib/roles/raw-file/ops-raw-file.c) endif() if (LWS_ROLE_CGI) @@ -785,12 +786,14 @@ endif() if (NOT LWS_WITHOUT_CLIENT) list(APPEND SOURCES + lib/core/connect.c lib/roles/http/client/client.c lib/roles/http/client/client-handshake.c) endif() if (NOT LWS_WITHOUT_SERVER) list(APPEND SOURCES + lib/core/adopt.c lib/roles/listen/ops-listen.c) endif() diff --git a/changelog b/changelog index 422813b1ab0fa900a919182433bc7ac43f8a559f..0931c8923505b8020c2a419f9cf8d09bb793cc22 100644 --- a/changelog +++ b/changelog @@ -1,6 +1,12 @@ Changelog --------- + - CHANGE: REMOVED: lws_client_connect() and lws_client_connect_extended() + compatibility apis for lws_client_connect_via_info() have been marked as + deprecated for several versions and are now removed. Use + lws_client_connect_via_info() instead. + + v3.0.0 ====== diff --git a/lib/core/adopt.c b/lib/core/adopt.c new file mode 100644 index 0000000000000000000000000000000000000000..b94faffbd16ee073edb2c6db2fbda56615f76fbb --- /dev/null +++ b/lib/core/adopt.c @@ -0,0 +1,407 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "core/private.h" + + +static int +lws_get_idlest_tsi(struct lws_context *context) +{ + unsigned int lowest = ~0; + int n = 0, hit = -1; + + for (; n < context->count_threads; n++) { + if ((unsigned int)context->pt[n].fds_count != + context->fd_limit_per_thread - 1 && + (unsigned int)context->pt[n].fds_count < lowest) { + lowest = context->pt[n].fds_count; + hit = n; + } + } + + return hit; +} + +struct lws * +lws_create_new_server_wsi(struct lws_vhost *vhost, int fixed_tsi) +{ + struct lws *new_wsi; + int n = fixed_tsi; + + if (n < 0) + n = lws_get_idlest_tsi(vhost->context); + + if (n < 0) { + lwsl_err("no space for new conn\n"); + return NULL; + } + + new_wsi = lws_zalloc(sizeof(struct lws), "new server wsi"); + if (new_wsi == NULL) { + lwsl_err("Out of memory for new connection\n"); + return NULL; + } + + new_wsi->tsi = n; + lwsl_debug("new wsi %p joining vhost %s, tsi %d\n", new_wsi, + vhost->name, new_wsi->tsi); + + new_wsi->vhost = vhost; + new_wsi->context = vhost->context; + new_wsi->pending_timeout = NO_PENDING_TIMEOUT; + new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW; + + /* initialize the instance struct */ + + lwsi_set_state(new_wsi, LRS_UNCONNECTED); + new_wsi->hdr_parsing_completed = 0; + +#ifdef LWS_WITH_TLS + new_wsi->tls.use_ssl = LWS_SSL_ENABLED(vhost); +#endif + + /* + * these can only be set once the protocol is known + * we set an un-established connection's protocol pointer + * to the start of the supported list, so it can look + * for matching ones during the handshake + */ + new_wsi->protocol = vhost->protocols; + new_wsi->user_space = NULL; + new_wsi->desc.sockfd = LWS_SOCK_INVALID; + new_wsi->position_in_fds_table = LWS_NO_FDS_POS; + + vhost->context->count_wsi_allocated++; + + /* + * outermost create notification for wsi + * no user_space because no protocol selection + */ + vhost->protocols[0].callback(new_wsi, LWS_CALLBACK_WSI_CREATE, + NULL, NULL, 0); + + return new_wsi; +} + + +/* if not a socket, it's a raw, non-ssl file descriptor */ + +LWS_VISIBLE struct lws * +lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type, + lws_sock_file_fd_type fd, const char *vh_prot_name, + struct lws *parent) +{ + struct lws_context *context = vh->context; + struct lws *new_wsi; + struct lws_context_per_thread *pt; + int n; + +#if defined(LWS_WITH_PEER_LIMITS) + struct lws_peer *peer = NULL; + + if (type & LWS_ADOPT_SOCKET) { + peer = lws_get_or_create_peer(vh, fd.sockfd); + + if (peer && context->ip_limit_wsi && + peer->count_wsi >= context->ip_limit_wsi) { + lwsl_notice("Peer reached wsi limit %d\n", + context->ip_limit_wsi); + lws_stats_atomic_bump(context, &context->pt[0], + LWSSTATS_C_PEER_LIMIT_WSI_DENIED, + 1); + return NULL; + } + } +#endif + + n = -1; + if (parent) + n = parent->tsi; + new_wsi = lws_create_new_server_wsi(vh, n); + if (!new_wsi) { + if (type & LWS_ADOPT_SOCKET) + compatible_close(fd.sockfd); + return NULL; + } +#if defined(LWS_WITH_PEER_LIMITS) + if (peer) + lws_peer_add_wsi(context, peer, new_wsi); +#endif + pt = &context->pt[(int)new_wsi->tsi]; + lws_stats_atomic_bump(context, pt, LWSSTATS_C_CONNECTIONS, 1); + + if (parent) { + new_wsi->parent = parent; + new_wsi->sibling_list = parent->child_list; + parent->child_list = new_wsi; + } + + new_wsi->desc = fd; + + if (vh_prot_name) { + new_wsi->protocol = lws_vhost_name_to_protocol(new_wsi->vhost, + vh_prot_name); + if (!new_wsi->protocol) { + lwsl_err("Protocol %s not enabled on vhost %s\n", + vh_prot_name, new_wsi->vhost->name); + goto bail; + } + if (lws_ensure_user_space(new_wsi)) { + lwsl_notice("OOM trying to get user_space\n"); + goto bail; + } + } + + if (!LWS_SSL_ENABLED(new_wsi->vhost) || !(type & LWS_ADOPT_SOCKET)) + type &= ~LWS_ADOPT_ALLOW_SSL; + + if (lws_role_call_adoption_bind(new_wsi, type, vh_prot_name)) { + lwsl_err("Unable to find a role that can adopt descriptor\n"); + goto bail; + } + + /* + * 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 the vhosts's default protocol, + * itself by default protocols[0] + */ + n = LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED; + if (!(type & LWS_ADOPT_HTTP)) { + if (!(type & LWS_ADOPT_SOCKET)) + n = LWS_CALLBACK_RAW_ADOPT_FILE; + else + n = LWS_CALLBACK_RAW_ADOPT; + } + + lwsl_debug("new wsi wsistate 0x%x\n", new_wsi->wsistate); + + if (context->event_loop_ops->accept) + context->event_loop_ops->accept(new_wsi); + + if (!(type & LWS_ADOPT_ALLOW_SSL)) { + lws_pt_lock(pt, __func__); + if (__insert_wsi_socket_into_fds(context, new_wsi)) { + lws_pt_unlock(pt); + lwsl_err("%s: fail inserting socket\n", __func__); + goto fail; + } + lws_pt_unlock(pt); + } else + if (lws_server_socket_service_ssl(new_wsi, fd.sockfd)) { + lwsl_info("%s: fail ssl negotiation\n", __func__); + goto fail; + } + + /* + * by deferring callback to this point, after insertion to fds, + * lws_callback_on_writable() can work from the callback + */ + if ((new_wsi->protocol->callback)( + new_wsi, n, new_wsi->user_space, NULL, 0)) + goto fail; + + /* role may need to do something after all adoption completed */ + + lws_role_call_adoption_bind(new_wsi, type | _LWS_ADOPT_FINISH, + vh_prot_name); + + lws_cancel_service_pt(new_wsi); + + return new_wsi; + +fail: + if (type & LWS_ADOPT_SOCKET) + lws_close_free_wsi(new_wsi, LWS_CLOSE_STATUS_NOSTATUS, + "adopt skt fail"); + + return NULL; + +bail: + lwsl_notice("%s: exiting on bail\n", __func__); + if (parent) + parent->child_list = new_wsi->sibling_list; + if (new_wsi->user_space) + lws_free(new_wsi->user_space); + lws_free(new_wsi); + compatible_close(fd.sockfd); + + return NULL; +} + +LWS_VISIBLE struct lws * +lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd) +{ + lws_sock_file_fd_type fd; + + fd.sockfd = accept_fd; + return lws_adopt_descriptor_vhost(vh, LWS_ADOPT_SOCKET | + LWS_ADOPT_HTTP | LWS_ADOPT_ALLOW_SSL, fd, NULL, NULL); +} + +LWS_VISIBLE struct lws * +lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd) +{ + return lws_adopt_socket_vhost(context->vhost_list, accept_fd); +} + +/* Common read-buffer adoption for lws_adopt_*_readbuf */ +static struct lws* +adopt_socket_readbuf(struct lws *wsi, const char *readbuf, size_t len) +{ + struct lws_context_per_thread *pt; + struct lws_pollfd *pfd; + int n; + + if (!wsi) + return NULL; + + if (!readbuf || len == 0) + return wsi; + + if (wsi->position_in_fds_table == LWS_NO_FDS_POS) + return wsi; + + pt = &wsi->context->pt[(int)wsi->tsi]; + + n = lws_buflist_append_segment(&wsi->buflist, (const uint8_t *)readbuf, + len); + if (n < 0) + goto bail; + if (n) + lws_dll_lws_add_front(&wsi->dll_buflist, &pt->dll_head_buflist); + + /* + * we can't process the initial read data until we can attach an ah. + * + * if one is available, get it and place the data in his ah rxbuf... + * wsi with ah that have pending rxbuf get auto-POLLIN service. + * + * no autoservice because we didn't get a chance to attach the + * readbuf data to wsi or ah yet, and we will do it next if we get + * the ah. + */ + if (wsi->http.ah || !lws_header_table_attach(wsi, 0)) { + + lwsl_notice("%s: calling service on readbuf ah\n", __func__); + + /* unlike a normal connect, we have the headers already + * (or the first part of them anyway). + * libuv won't come back and service us without a network + * event, so we need to do the header service right here. + */ + pfd = &pt->fds[wsi->position_in_fds_table]; + pfd->revents |= LWS_POLLIN; + lwsl_err("%s: calling service\n", __func__); + if (lws_service_fd_tsi(wsi->context, pfd, wsi->tsi)) + /* service closed us */ + return NULL; + + return wsi; + } + lwsl_err("%s: deferring handling ah\n", __func__); + + return wsi; + +bail: + lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, + "adopt skt readbuf fail"); + + return NULL; +} + +LWS_EXTERN struct lws * +lws_create_adopt_udp(struct lws_vhost *vhost, int port, int flags, + const char *protocol_name, struct lws *parent_wsi) +{ + lws_sock_file_fd_type sock; + struct addrinfo h, *r, *rp; + struct lws *wsi = NULL; + char buf[16]; + int n; + + memset(&h, 0, sizeof(h)); + h.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + h.ai_socktype = SOCK_DGRAM; + h.ai_protocol = IPPROTO_UDP; + h.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; + + lws_snprintf(buf, sizeof(buf), "%u", port); + n = getaddrinfo(NULL, buf, &h, &r); + if (n) { + lwsl_info("%s: getaddrinfo error: %s\n", __func__, + gai_strerror(n)); + goto bail; + } + + for (rp = r; rp; rp = rp->ai_next) { + sock.sockfd = socket(rp->ai_family, rp->ai_socktype, + rp->ai_protocol); + if (sock.sockfd >= 0) + break; + } + if (!rp) { + lwsl_err("%s: unable to create INET socket\n", __func__); + goto bail1; + } + + if ((flags & LWS_CAUDP_BIND) && bind(sock.sockfd, rp->ai_addr, +#if defined(_WIN32) + (int)rp->ai_addrlen +#else + rp->ai_addrlen +#endif + ) == -1) { + lwsl_err("%s: bind failed\n", __func__); + goto bail2; + } + + wsi = lws_adopt_descriptor_vhost(vhost, LWS_ADOPT_RAW_SOCKET_UDP, sock, + protocol_name, parent_wsi); + if (!wsi) + lwsl_err("%s: udp adoption failed\n", __func__); + +bail2: + if (!wsi) + close((int)sock.sockfd); +bail1: + freeaddrinfo(r); + +bail: + return wsi; +} + +LWS_VISIBLE struct lws * +lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd, + const char *readbuf, size_t len) +{ + return adopt_socket_readbuf(lws_adopt_socket(context, accept_fd), + readbuf, len); +} + +LWS_VISIBLE struct lws * +lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost, + lws_sockfd_type accept_fd, + const char *readbuf, size_t len) +{ + return adopt_socket_readbuf(lws_adopt_socket_vhost(vhost, accept_fd), + readbuf, len); +} diff --git a/lib/core/connect.c b/lib/core/connect.c new file mode 100644 index 0000000000000000000000000000000000000000..ace3dc18eec4fafbd9f5f898d89dfd3d06cbe361 --- /dev/null +++ b/lib/core/connect.c @@ -0,0 +1,229 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include "core/private.h" + +void +lws_client_stash_destroy(struct lws *wsi) +{ + if (!wsi || !wsi->stash) + return; + + lws_free_set_NULL(wsi->stash->address); + lws_free_set_NULL(wsi->stash->path); + lws_free_set_NULL(wsi->stash->host); + lws_free_set_NULL(wsi->stash->origin); + lws_free_set_NULL(wsi->stash->protocol); + lws_free_set_NULL(wsi->stash->method); + lws_free_set_NULL(wsi->stash->iface); + lws_free_set_NULL(wsi->stash->alpn); + + lws_free_set_NULL(wsi->stash); +} + +LWS_VISIBLE struct lws * +lws_client_connect_via_info(const struct lws_client_connect_info *i) +{ + struct lws *wsi; + const struct lws_protocols *p; + const char *local = i->protocol; + + if (i->context->requested_kill) + return NULL; + + if (!i->context->protocol_init_done) + lws_protocol_init(i->context); + /* + * If we have .local_protocol_name, use it to select the local protocol + * handler to bind to. Otherwise use .protocol if http[s]. + */ + if (i->local_protocol_name) + local = i->local_protocol_name; + + /* PHASE 1: create a bare wsi */ + + wsi = lws_zalloc(sizeof(struct lws), "client wsi"); + if (wsi == NULL) + goto bail; + + wsi->context = i->context; + wsi->desc.sockfd = LWS_SOCK_INVALID; + + /* + * PHASE 2: Choose an initial role for the wsi and do role-specific init + * + * Note the initial role may not reflect the final role, eg, + * we may want ws, but first we have to go through h1 to get that + */ + + lws_role_call_client_bind(wsi, i); + + /* + * PHASE 3: fill up the wsi with stuff from the connect_info as far as + * it can go. It's uncertain because not only is our connection + * going to complete asynchronously, we might have bound to h1 and not + * even be able to get ahold of an ah immediately. + */ + + wsi->user_space = NULL; + wsi->pending_timeout = NO_PENDING_TIMEOUT; + wsi->position_in_fds_table = LWS_NO_FDS_POS; + wsi->c_port = i->port; + wsi->vhost = i->vhost; + if (!wsi->vhost) + wsi->vhost = i->context->vhost_list; + + if (!wsi->vhost) { + lwsl_err("%s: No vhost in the context\n", __func__); + + goto bail; + } + + wsi->protocol = &wsi->vhost->protocols[0]; + wsi->client_pipeline = !!(i->ssl_connection & LCCSCF_PIPELINE); + + if (local) { + lwsl_info("%s: protocol binding to %s\n", __func__, local); + p = lws_vhost_name_to_protocol(wsi->vhost, local); + if (p) + wsi->protocol = p; + } + + /* + * PHASE 4: handle external user_space now, generic alloc is done in + * role finalization + */ + + if (wsi && !wsi->user_space && i->userdata) { + wsi->user_space_externally_allocated = 1; + wsi->user_space = i->userdata; + } + +#if defined(LWS_WITH_TLS) + wsi->tls.use_ssl = i->ssl_connection; +#else + if (i->ssl_connection & LCCSCF_USE_SSL) { + lwsl_err("%s: lws not configured for tls\n", __func__); + goto bail; + } +#endif + + /* + * PHASE 5: stash the things from connect_info that we can't process + * right now, eg, if http binding, without an ah. If h1 and no ah, we + * will go on the ah waiting list and process those things later (after + * the connect_info and maybe the things pointed to have gone out of + * scope) + * + * However these things are stashed in a generic way at this point, + * with no relationship to http or ah + */ + + wsi->stash = lws_zalloc(sizeof(*wsi->stash), "client stash"); + if (!wsi->stash) { + lwsl_err("%s: OOM\n", __func__); + goto bail1; + } + + wsi->stash->address = lws_strdup(i->address); + wsi->stash->path = lws_strdup(i->path); + wsi->stash->host = lws_strdup(i->host); + + if (!wsi->stash->address || !wsi->stash->path || !wsi->stash->host) + goto bail1; + + if (i->origin) { + wsi->stash->origin = lws_strdup(i->origin); + if (!wsi->stash->origin) + goto bail1; + } + if (i->protocol) { + wsi->stash->protocol = lws_strdup(i->protocol); + if (!wsi->stash->protocol) + goto bail1; + } + if (i->method) { + wsi->stash->method = lws_strdup(i->method); + if (!wsi->stash->method) + goto bail1; + } + if (i->iface) { + wsi->stash->iface = lws_strdup(i->iface); + if (!wsi->stash->iface) + goto bail1; + } + if (i->alpn) { + wsi->stash->alpn = lws_strdup(i->alpn); + if (!wsi->stash->alpn) + goto bail1; + } + + /* + * PHASE 6: Do any role-specific finalization processing. We can still + * see important info things via wsi->stash + */ + + if (wsi->role_ops->client_bind) { + int n = wsi->role_ops->client_bind(wsi, NULL); + + if (n < 0) + /* we didn't survive, wsi is freed */ + goto bail2; + + if (n) + /* something else failed, wsi needs freeing */ + goto bail; + } + + /* let the caller's optional wsi storage have the wsi we created */ + + if (i->pwsi) + *i->pwsi = wsi; + + if (i->parent_wsi) { + lwsl_info("%s: created child %p of parent %p\n", __func__, + wsi, i->parent_wsi); + wsi->parent = i->parent_wsi; + wsi->sibling_list = i->parent_wsi->child_list; + i->parent_wsi->child_list = wsi; + } +#ifdef LWS_WITH_HTTP_PROXY + if (i->uri_replace_to) + wsi->http.rw = lws_rewrite_create(wsi, html_parser_cb, + i->uri_replace_from, + i->uri_replace_to); +#endif + + return wsi; + +bail1: + lws_client_stash_destroy(wsi); + +bail: + lws_free(wsi); +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) +bail2: +#endif + if (i->pwsi) + *i->pwsi = NULL; + + return NULL; +} diff --git a/lib/core/context.c b/lib/core/context.c index db9151b95f2ba76cddf9944a074bc60ef58142b6..dd4bfd20ea23289d22786ace45fb610b4dc021e0 100644 --- a/lib/core/context.c +++ b/lib/core/context.c @@ -86,6 +86,57 @@ lws_role_call_alpn_negotiated(struct lws *wsi, const char *alpn) return 0; } +#if !defined(LWS_WITHOUT_SERVER) +int +lws_role_call_adoption_bind(struct lws *wsi, int type, const char *prot) +{ + LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) + if (ar->adoption_bind) + if (ar->adoption_bind(wsi, type, prot)) + return 0; + LWS_FOR_EVERY_AVAILABLE_ROLE_END; + + /* fall back to raw socket role if, eg, h1 not configured */ + + if (role_ops_raw_skt.adoption_bind && + role_ops_raw_skt.adoption_bind(wsi, type, prot)) + return 0; + + /* fall back to raw file role if, eg, h1 not configured */ + + if (role_ops_raw_file.adoption_bind && + role_ops_raw_file.adoption_bind(wsi, type, prot)) + return 0; + + return 1; +} +#endif + +#if !defined(LWS_WITHOUT_CLIENT) +int +lws_role_call_client_bind(struct lws *wsi, + const struct lws_client_connect_info *i) +{ + LWS_FOR_EVERY_AVAILABLE_ROLE_START(ar) + if (ar->client_bind) { + int m = ar->client_bind(wsi, i); + if (m < 0) + return m; + if (m) + return 0; + } + LWS_FOR_EVERY_AVAILABLE_ROLE_END; + + /* fall back to raw socket role if, eg, h1 not configured */ + + if (role_ops_raw_skt.client_bind && + role_ops_raw_skt.client_bind(wsi, i)) + return 0; + + return 1; +} +#endif + static const char * const mount_protocols[] = { "http://", "https://", diff --git a/lib/core/libwebsockets.c b/lib/core/libwebsockets.c index 0da02b17e491ca5143ac65523db1a674d23a09b8..e560b36b06f94b79ac1670ca34127dc23b4468b7 100644 --- a/lib/core/libwebsockets.c +++ b/lib/core/libwebsockets.c @@ -1149,9 +1149,6 @@ lws_get_peer_simple(struct lws *wsi, char *name, int namelen) wsi = lws_get_network_wsi(wsi); - if (wsi->parent_carries_io) - wsi = wsi->parent; - #ifdef LWS_WITH_IPV6 if (LWS_IPV6_ENABLED(wsi->vhost)) { len = sizeof(sin6); @@ -2158,12 +2155,6 @@ lws_get_child(const struct lws *wsi) return wsi->child_list; } -LWS_VISIBLE LWS_EXTERN void -lws_set_parent_carries_io(struct lws *wsi) -{ - wsi->parent_carries_io = 1; -} - LWS_VISIBLE LWS_EXTERN void * lws_get_opaque_parent_data(const struct lws *wsi) { @@ -2350,6 +2341,17 @@ lws_parse_uri(char *p, const char **prot, const char **ads, int *port, return 0; } +char * +lws_strdup(const char *s) +{ + char *d = lws_malloc(strlen(s) + 1, "strdup"); + + if (d) + strcpy(d, s); + + return d; +} + #if defined(LWS_WITHOUT_EXTENSIONS) /* we need to provide dummy callbacks for internal exts @@ -2619,73 +2621,6 @@ lws_get_addr_scope(const char *ipaddr) } #endif -#if !defined(LWS_NO_SERVER) - -LWS_EXTERN struct lws * -lws_create_adopt_udp(struct lws_vhost *vhost, int port, int flags, - const char *protocol_name, struct lws *parent_wsi) -{ - lws_sock_file_fd_type sock; - struct addrinfo h, *r, *rp; - struct lws *wsi = NULL; - char buf[16]; - int n; - - memset(&h, 0, sizeof(h)); - h.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ - h.ai_socktype = SOCK_DGRAM; - h.ai_protocol = IPPROTO_UDP; - h.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; - - lws_snprintf(buf, sizeof(buf), "%u", port); - n = getaddrinfo(NULL, buf, &h, &r); - if (n) { - lwsl_info("%s: getaddrinfo error: %s\n", __func__, - gai_strerror(n)); - goto bail; - } - - for (rp = r; rp; rp = rp->ai_next) { - sock.sockfd = socket(rp->ai_family, rp->ai_socktype, - rp->ai_protocol); - if (sock.sockfd >= 0) - break; - } - if (!rp) { - lwsl_err("%s: unable to create INET socket\n", __func__); - goto bail1; - } - - if ((flags & LWS_CAUDP_BIND) && bind(sock.sockfd, rp->ai_addr, -#if defined(_WIN32) - (int)rp->ai_addrlen -#else - rp->ai_addrlen -#endif - ) == -1) { - lwsl_err("%s: bind failed\n", __func__); - goto bail2; - } - - wsi = lws_adopt_descriptor_vhost(vhost, LWS_ADOPT_RAW_SOCKET_UDP, sock, - protocol_name, parent_wsi); - if (!wsi) - lwsl_err("%s: udp adoption failed\n", __func__); - -bail2: - if (!wsi) - close((int)sock.sockfd); -bail1: - freeaddrinfo(r); - -bail: - return wsi; -} - -#endif - - - static const char *hex = "0123456789ABCDEF"; LWS_VISIBLE LWS_EXTERN const char * diff --git a/lib/core/output.c b/lib/core/output.c index e2ff18ef275a1fa27737109a4a8b2146ea8873c0..6aa8d057b5721c04a333b693393a674bf5ba005b 100644 --- a/lib/core/output.c +++ b/lib/core/output.c @@ -187,23 +187,6 @@ LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf, size_t len, { struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; - if (wsi->parent_carries_io) { - struct lws_write_passthru pas; - - pas.buf = buf; - pas.len = len; - pas.wp = wp; - pas.wsi = wsi; - - if (wsi->parent->protocol->callback(wsi->parent, - LWS_CALLBACK_CHILD_WRITE_VIA_PARENT, - wsi->parent->user_space, - (void *)&pas, 0)) - return 1; - - return (int)len; - } - lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_API_LWS_WRITE, 1); if ((int)len < 0) { diff --git a/lib/core/pollfd.c b/lib/core/pollfd.c index 2a632ce8ec3ea60607ef03dceeab0838cf1d53f6..24f7c7ab765ecb883a37edfb3e49fa8f6d8239b5 100644 --- a/lib/core/pollfd.c +++ b/lib/core/pollfd.c @@ -298,11 +298,6 @@ __remove_wsi_socket_from_fds(struct lws *wsi) int v; int m, ret = 0; - if (wsi->parent_carries_io) { - lws_same_vh_protocol_remove(wsi); - return 0; - } - #if !defined(_WIN32) if (wsi->desc.sockfd - lws_plat_socket_offset() > context->max_fds) { lwsl_err("fd %d too high (%d)\n", wsi->desc.sockfd, @@ -426,7 +421,6 @@ LWS_VISIBLE int lws_callback_on_writable(struct lws *wsi) { struct lws_context_per_thread *pt; - int n; if (lwsi_state(wsi) == LRS_SHUTDOWN) return 0; @@ -436,22 +430,6 @@ lws_callback_on_writable(struct lws *wsi) pt = &wsi->context->pt[(int)wsi->tsi]; - if (wsi->parent_carries_io) { -#if defined(LWS_WITH_STATS) - if (!wsi->active_writable_req_us) { - wsi->active_writable_req_us = time_in_microseconds(); - lws_stats_atomic_bump(wsi->context, pt, - LWSSTATS_C_WRITEABLE_CB_EFF_REQ, 1); - } -#endif - n = lws_callback_on_writable(wsi->parent); - if (n < 0) - return n; - - wsi->parent_pending_cb_on_writable = 1; - return 1; - } - lws_stats_atomic_bump(wsi->context, pt, LWSSTATS_C_WRITEABLE_CB_REQ, 1); #if defined(LWS_WITH_STATS) if (!wsi->active_writable_req_us) { diff --git a/lib/core/private.h b/lib/core/private.h index 0597df4b6b2082ebfa04107003a73ea46047b3d9..2e64000017c7b8da41058cdb4b5c3ef445c9e088 100644 --- a/lib/core/private.h +++ b/lib/core/private.h @@ -1120,7 +1120,6 @@ struct lws { unsigned int waiting_to_send_close_frame:1; unsigned int close_needs_ack:1; unsigned int ipv6:1; - unsigned int parent_carries_io:1; unsigned int parent_pending_cb_on_writable:1; unsigned int cgi_stdout_zero_length:1; unsigned int seen_zero_length_recv:1; @@ -1186,6 +1185,9 @@ struct lws { volatile char leave_pollout_active; }; +LWS_EXTERN char * +lws_strdup(const char *s); + #define lws_is_flowcontrolled(w) (!!(wsi->rxflow_bitmap)) void @@ -1297,7 +1299,7 @@ LWS_EXTERN int lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd); LWS_EXTERN struct lws * -lws_client_connect_via_info2(struct lws *wsi); +lws_http_client_connect_via_info2(struct lws *wsi); @@ -1731,7 +1733,8 @@ lws_generate_client_ws_handshake(struct lws *wsi, char *p); int lws_client_ws_upgrade(struct lws *wsi, const char **cce); int -lws_create_client_ws_object(struct lws_client_connect_info *i, struct lws *wsi); +lws_create_client_ws_object(const struct lws_client_connect_info *i, + struct lws *wsi); int lws_alpn_comma_to_openssl(const char *comma, uint8_t *os, int len); int @@ -1745,6 +1748,9 @@ void lws_destroy_event_pipe(struct lws *wsi); void lws_context_destroy2(struct lws_context *context); +int +lws_role_call_client_bind(struct lws *wsi, + const struct lws_client_connect_info *i); #ifdef __cplusplus }; diff --git a/lib/core/service.c b/lib/core/service.c index 6523058814de992529002e92cbf60f8ed834ba51..7ecc769e0c606f0ab468c1ecbb19c967cde9a49d 100644 --- a/lib/core/service.c +++ b/lib/core/service.c @@ -126,13 +126,6 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd) /* one shot */ - if (wsi->parent_carries_io) { - vwsi->handling_pollout = 0; - vwsi->leave_pollout_active = 0; - - return lws_callback_as_writeable(wsi); - } - if (pollfd) { int eff = vwsi->leave_pollout_active; diff --git a/lib/event-libs/libuv/libuv.c b/lib/event-libs/libuv/libuv.c index 57947d4e239c803cc796c32562e73788e1b99d2a..b04cf556fe32db98bf8e949bf05389d3dc60299e 100644 --- a/lib/event-libs/libuv/libuv.c +++ b/lib/event-libs/libuv/libuv.c @@ -562,7 +562,7 @@ elops_destroy_context2_uv(struct lws_context *context) static int elops_wsi_logical_close_uv(struct lws *wsi) { - if (wsi->parent_carries_io || !lws_socket_is_valid(wsi->desc.sockfd)) + if (!lws_socket_is_valid(wsi->desc.sockfd)) return 0; if (wsi->listener || wsi->event_pipe) { diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index b125d1b888aba691a232d6b606695b6443898c41..c0a50bca88330bce8f1b3c47a821841675167dbd 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -1565,13 +1565,6 @@ enum lws_callback_reasons { * destroyed. in is the child wsi. */ - LWS_CALLBACK_CHILD_WRITE_VIA_PARENT = 68, - /**< Child has been marked with parent_carries_io attribute, so - * lws_write directs the to this callback at the parent, - * in is a struct lws_write_passthru containing the args - * the lws_write() was called with. - */ - /* --------------------------------------------------------------------- * ----- Callbacks related to TLS certificate management ----- */ @@ -3490,61 +3483,7 @@ struct lws_client_connect_info { * information provided in ccinfo. */ LWS_VISIBLE LWS_EXTERN struct lws * -lws_client_connect_via_info(struct lws_client_connect_info * ccinfo); - -/** - * lws_client_connect() - Connect to another websocket server - * \deprecated DEPRECATED use lws_client_connect_via_info - * \param clients: Websocket context - * \param address: Remote server address, eg, "myserver.com" - * \param port: Port to connect to on the remote server, eg, 80 - * \param ssl_connection: 0 = ws://, 1 = wss:// encrypted, 2 = wss:// allow self - * signed certs - * \param path: Websocket path on server - * \param host: Hostname on server - * \param origin: Socket origin name - * \param protocol: Comma-separated list of protocols being asked for from - * the server, or just one. The server will pick the one it - * likes best. If you don't want to specify a protocol, which is - * legal, use NULL here. - * \param ietf_version_or_minus_one: -1 to ask to connect using the default, latest - * protocol supported, or the specific protocol ordinal - * - * This function creates a connection to a remote server - */ -/* deprecated, use lws_client_connect_via_info() */ -LWS_VISIBLE LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT -lws_client_connect(struct lws_context *clients, const char *address, - int port, int ssl_connection, const char *path, - const char *host, const char *origin, const char *protocol, - int ietf_version_or_minus_one) LWS_WARN_DEPRECATED; -/* deprecated, use lws_client_connect_via_info() */ -/** - * lws_client_connect_extended() - Connect to another websocket server - * \deprecated DEPRECATED use lws_client_connect_via_info - * \param clients: Websocket context - * \param address: Remote server address, eg, "myserver.com" - * \param port: Port to connect to on the remote server, eg, 80 - * \param ssl_connection: 0 = ws://, 1 = wss:// encrypted, 2 = wss:// allow self - * signed certs - * \param path: Websocket path on server - * \param host: Hostname on server - * \param origin: Socket origin name - * \param protocol: Comma-separated list of protocols being asked for from - * the server, or just one. The server will pick the one it - * likes best. - * \param ietf_version_or_minus_one: -1 to ask to connect using the default, latest - * protocol supported, or the specific protocol ordinal - * \param userdata: Pre-allocated user data - * - * This function creates a connection to a remote server - */ -LWS_VISIBLE LWS_EXTERN struct lws * LWS_WARN_UNUSED_RESULT -lws_client_connect_extended(struct lws_context *clients, const char *address, - int port, int ssl_connection, const char *path, - const char *host, const char *origin, - const char *protocol, int ietf_version_or_minus_one, - void *userdata) LWS_WARN_DEPRECATED; +lws_client_connect_via_info(const struct lws_client_connect_info *ccinfo); /** * lws_init_vhost_client_ssl() - also enable client SSL on an existing vhost @@ -5283,9 +5222,6 @@ typedef enum { LWS_ADOPT_HTTP = 1, /* flag: absent implies RAW */ LWS_ADOPT_SOCKET = 2, /* flag: absent implies file descr */ LWS_ADOPT_ALLOW_SSL = 4, /* flag: if set requires LWS_ADOPT_SOCKET */ - LWS_ADOPT_WS_PARENTIO = 8, /* flag: ws mode parent handles IO - * if given must be only flag - * wsi put directly into ws mode */ LWS_ADOPT_FLAG_UDP = 16, /* flag: socket is UDP */ LWS_ADOPT_RAW_SOCKET_UDP = LWS_ADOPT_SOCKET | LWS_ADOPT_FLAG_UDP, @@ -5933,15 +5869,6 @@ lws_get_child(const struct lws *wsi); LWS_VISIBLE LWS_EXTERN const struct lws_udp * LWS_WARN_UNUSED_RESULT lws_get_udp(const struct lws *wsi); -/** - * lws_parent_carries_io() - mark wsi as needing to send messages via parent - * - * \param wsi: child lws connection - */ - -LWS_VISIBLE LWS_EXTERN void -lws_set_parent_carries_io(struct lws *wsi); - LWS_VISIBLE LWS_EXTERN void * lws_get_opaque_parent_data(const struct lws *wsi); diff --git a/lib/roles/cgi/ops-cgi.c b/lib/roles/cgi/ops-cgi.c index 8555f7baa5180b8b525b48a2c87cbdc3da87876a..0a3cc5cbb3cc117e078afaa8db6ca6da923c101b 100644 --- a/lib/roles/cgi/ops-cgi.c +++ b/lib/roles/cgi/ops-cgi.c @@ -96,6 +96,8 @@ struct lws_role_ops role_ops_cgi = { /* close_role */ NULL, /* close_kill_connection */ NULL, /* destroy_role */ NULL, + /* adoption_bind */ NULL, + /* client_bind */ NULL, /* writeable cb clnt, srv */ { 0, 0 }, /* close cb clnt, srv */ { 0, 0 }, /* file_handle */ 0, diff --git a/lib/roles/h1/ops-h1.c b/lib/roles/h1/ops-h1.c index 49dfe4b47f241747c215424a92150ab7c0b4aae0..938642502431f38ddae20241f1e14692596f1995 100644 --- a/lib/roles/h1/ops-h1.c +++ b/lib/roles/h1/ops-h1.c @@ -659,6 +659,133 @@ rops_destroy_role_h1(struct lws *wsi) return 0; } +#if !defined(LWS_NO_SERVER) + +static int +rops_adoption_bind_h1(struct lws *wsi, int type, const char *vh_prot_name) +{ + if (!(type & LWS_ADOPT_HTTP)) + return 0; /* no match */ + + + if (type & _LWS_ADOPT_FINISH) { + if (!lws_header_table_attach(wsi, 0)) + lwsl_debug("Attached ah immediately\n"); + else + lwsl_info("%s: waiting for ah\n", __func__); + + return 1; + } + + lws_role_transition(wsi, LWSIFR_SERVER, type & LWS_ADOPT_ALLOW_SSL ? + LRS_SSL_INIT : LRS_HEADERS, &role_ops_h1); + + if (!vh_prot_name) + wsi->protocol = &wsi->vhost->protocols[ + wsi->vhost->default_protocol_index]; + + /* the transport is accepted... give him time to negotiate */ + lws_set_timeout(wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER, + wsi->context->timeout_secs); + + return 1; /* bound */ +} + +#endif + +#if !defined(LWS_NO_CLIENT) + +static const char * const http_methods[] = { + "GET", "POST", "OPTIONS", "PUT", "PATCH", "DELETE", "CONNECT" +}; + +static int +rops_client_bind_h1(struct lws *wsi, const struct lws_client_connect_info *i) +{ + int n; + + if (!i) { + /* we are finalizing an already-selected role */ + + /* + * If we stay in http, assuming there wasn't already-set + * external user_space, since we know our initial protocol + * we can assign the user space now, otherwise do it after the + * ws subprotocol negotiated + */ + if (!wsi->user_space && wsi->stash->method) + if (lws_ensure_user_space(wsi)) + return 1; + + /* + * For ws, default to http/1.1 only. If i->alpn had been set + * though, defer to whatever he has set in there (eg, "h2"). + * + * The problem is he has to commit to h2 before he can find + * out if the server has the SETTINGS for ws-over-h2 enabled; + * if not then ws is not possible on that connection. So we + * only try h2 if he assertively said to use h2 alpn, otherwise + * ws implies alpn restriction to h1. + */ + if (!wsi->stash->method && !wsi->stash->alpn) { + wsi->stash->alpn = lws_strdup("http/1.1"); + if (!wsi->stash->alpn) + return 1; + } + + /* if we went on the ah waiting list, it's ok, we can wait. + * + * When we do get the ah, now or later, he will end up at + * lws_http_client_connect_via_info2(). + */ + if (lws_header_table_attach(wsi, 0) < 0) + /* + * if we failed here, the connection is already closed + * and freed. + */ + return -1; + + return 0; + } + + /* + * Clients that want to be h1, h2, or ws all start out as h1 + * (we don't yet know if the server supports h2 or ws) + */ + + if (!i->method) { /* websockets */ +#if defined(LWS_ROLE_WS) + if (lws_create_client_ws_object(i, wsi)) + goto fail_wsi; +#else + lwsl_err("%s: ws role not configured\n", __func__); + + goto fail_wsi; +#endif + goto bind_h1; + } + + /* if a recognized http method, bind to it */ + + for (n = 0; n < (int)LWS_ARRAY_SIZE(http_methods); n++) + if (!strcmp(i->method, http_methods[n])) + goto bind_h1; + + /* other roles may bind to it */ + + return 0; /* no match */ + +bind_h1: + /* assert the mode and union status (hdr) clearly */ + lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED, &role_ops_h1); + + return 1; /* matched */ + +fail_wsi: + return -1; +} +#endif + struct lws_role_ops role_ops_h1 = { /* role name */ "h1", /* alpn id */ "http/1.1", @@ -680,6 +807,16 @@ struct lws_role_ops role_ops_h1 = { /* close_role */ NULL, /* close_kill_connection */ NULL, /* destroy_role */ rops_destroy_role_h1, +#if !defined(LWS_NO_SERVER) + /* adoption_bind */ rops_adoption_bind_h1, +#else + NULL, +#endif +#if !defined(LWS_NO_CLIENT) + /* client_bind */ rops_client_bind_h1, +#else + NULL, +#endif /* writeable cb clnt, srv */ { LWS_CALLBACK_CLIENT_HTTP_WRITEABLE, LWS_CALLBACK_HTTP_WRITEABLE }, /* close cb clnt, srv */ { LWS_CALLBACK_CLOSED_CLIENT_HTTP, diff --git a/lib/roles/h2/ops-h2.c b/lib/roles/h2/ops-h2.c index 0e0afeeacc19fba6614c84bec2553bec80bb1153..e4eebbb6a275341056ab5dd723dad2daf6219f27 100644 --- a/lib/roles/h2/ops-h2.c +++ b/lib/roles/h2/ops-h2.c @@ -1076,6 +1076,8 @@ struct lws_role_ops role_ops_h2 = { /* close_role */ NULL, /* close_kill_connection */ rops_close_kill_connection_h2, /* destroy_role */ rops_destroy_role_h2, + /* adoption_bind */ NULL, + /* client_bind */ NULL, /* writeable cb clnt, srv */ { LWS_CALLBACK_CLIENT_HTTP_WRITEABLE, LWS_CALLBACK_HTTP_WRITEABLE }, /* close cb clnt, srv */ { LWS_CALLBACK_CLOSED_CLIENT_HTTP, diff --git a/lib/roles/http/client/client-handshake.c b/lib/roles/http/client/client-handshake.c index 4830fc9ecae378374ef11c4ebb4ececd19d747b7..36cf43192d3286dc9e7f91820f2403dba5564ba8 100644 --- a/lib/roles/http/client/client-handshake.c +++ b/lib/roles/http/client/client-handshake.c @@ -846,252 +846,8 @@ html_parser_cb(const hubbub_token *token, void *pw) #endif -static char * -lws_strdup(const char *s) -{ - char *d = lws_malloc(strlen(s) + 1, "strdup"); - - if (d) - strcpy(d, s); - - return d; -} - -void -lws_client_stash_destroy(struct lws *wsi) -{ - if (!wsi || !wsi->stash) - return; - - lws_free_set_NULL(wsi->stash->address); - lws_free_set_NULL(wsi->stash->path); - lws_free_set_NULL(wsi->stash->host); - lws_free_set_NULL(wsi->stash->origin); - lws_free_set_NULL(wsi->stash->protocol); - lws_free_set_NULL(wsi->stash->method); - lws_free_set_NULL(wsi->stash->iface); - lws_free_set_NULL(wsi->stash->alpn); - - lws_free_set_NULL(wsi->stash); -} - -LWS_VISIBLE struct lws * -lws_client_connect_via_info(struct lws_client_connect_info *i) -{ - struct lws *wsi; - const struct lws_protocols *p; - const char *local = i->protocol; - - if (i->context->requested_kill) - return NULL; - - if (!i->context->protocol_init_done) - lws_protocol_init(i->context); - /* - * If we have .local_protocol_name, use it to select the - * local protocol handler to bind to. Otherwise use .protocol if - * http[s]. - */ - if (i->local_protocol_name) - local = i->local_protocol_name; - - wsi = lws_zalloc(sizeof(struct lws), "client wsi"); - if (wsi == NULL) - goto bail; - - wsi->context = i->context; -#if defined(LWS_ROLE_H1) - /* assert the mode and union status (hdr) clearly */ - lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED, &role_ops_h1); -#else - lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED, &role_ops_raw_skt); -#endif - wsi->desc.sockfd = LWS_SOCK_INVALID; - - /* 1) fill up the wsi with stuff from the connect_info as far as it - * can go. It's because not only is our connection async, we might - * not even be able to get ahold of an ah at this point. - */ - - if (!i->method) /* ie, ws */ -#if defined(LWS_ROLE_WS) - if (lws_create_client_ws_object(i, wsi)) - return NULL; -#else - return NULL; -#endif - - wsi->user_space = NULL; - wsi->pending_timeout = NO_PENDING_TIMEOUT; - wsi->position_in_fds_table = LWS_NO_FDS_POS; - wsi->c_port = i->port; - wsi->vhost = i->vhost; - if (!wsi->vhost) - wsi->vhost = i->context->vhost_list; - - if (!wsi->vhost) { - lwsl_err("At least one vhost in the context is required\n"); - - goto bail; - } - - wsi->protocol = &wsi->vhost->protocols[0]; - wsi->client_pipeline = !!(i->ssl_connection & LCCSCF_PIPELINE); - - /* reasonable place to start */ - lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED, -#if defined(LWS_ROLE_H1) - &role_ops_h1); -#else - &role_ops_raw_skt); -#endif - - /* - * 1) for http[s] connection, allow protocol selection by name - * 2) for ws[s], if local_protocol_name given also use it for - * local protocol binding... this defeats the server - * protocol negotiation if so - * - * Otherwise leave at protocols[0]... the server will tell us - * which protocol we are associated with since we can give it a - * list. - */ - if (/*(i->method || i->local_protocol_name) && */local) { - lwsl_info("binding to %s\n", local); - p = lws_vhost_name_to_protocol(wsi->vhost, local); - if (p) - wsi->protocol = p; - } - - if (wsi && !wsi->user_space && i->userdata) { - wsi->user_space_externally_allocated = 1; - wsi->user_space = i->userdata; - } else - /* if we stay in http, we can assign the user space now, - * otherwise do it after the protocol negotiated - */ - if (i->method) - if (lws_ensure_user_space(wsi)) - goto bail; - -#if defined(LWS_WITH_TLS) - wsi->tls.use_ssl = i->ssl_connection; -#else - if (i->ssl_connection & LCCSCF_USE_SSL) { - lwsl_err("libwebsockets not configured for ssl\n"); - goto bail; - } -#endif - - /* 2) stash the things from connect_info that we can't process without - * an ah. Because if no ah, we will go on the ah waiting list and - * process those things later (after the connect_info and maybe the - * things pointed to have gone out of scope. - */ - - wsi->stash = lws_zalloc(sizeof(*wsi->stash), "client stash"); - if (!wsi->stash) { - lwsl_err("%s: OOM\n", __func__); - goto bail1; - } - - wsi->stash->address = lws_strdup(i->address); - wsi->stash->path = lws_strdup(i->path); - wsi->stash->host = lws_strdup(i->host); - - if (!wsi->stash->address || !wsi->stash->path || !wsi->stash->host) - goto bail1; - - if (i->origin) { - wsi->stash->origin = lws_strdup(i->origin); - if (!wsi->stash->origin) - goto bail1; - } - if (i->protocol) { - wsi->stash->protocol = lws_strdup(i->protocol); - if (!wsi->stash->protocol) - goto bail1; - } - if (i->method) { - wsi->stash->method = lws_strdup(i->method); - if (!wsi->stash->method) - goto bail1; - } - if (i->iface) { - wsi->stash->iface = lws_strdup(i->iface); - if (!wsi->stash->iface) - goto bail1; - } - /* - * For ws, default to http/1.1 only. If i->alpn is set, defer to - * whatever he has set in there (eg, "h2"). - * - * The problem is he has to commit to h2 before he can find out if the - * server has the SETTINGS for ws-over-h2 enabled; if not then ws is - * not possible on that connection. So we only try it if he - * assertively said to use h2 alpn. - */ - if (!i->method && !i->alpn) { - wsi->stash->alpn = lws_strdup("http/1.1"); - if (!wsi->stash->alpn) - goto bail1; - } else - if (i->alpn) { - wsi->stash->alpn = lws_strdup(i->alpn); - if (!wsi->stash->alpn) - goto bail1; - } - - if (i->pwsi) - *i->pwsi = wsi; - -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - /* if we went on the waiting list, no probs just return the wsi - * when we get the ah, now or later, he will call - * lws_client_connect_via_info2() below. - */ - if (lws_header_table_attach(wsi, 0) < 0) { - /* - * if we failed here, the connection is already closed - * and freed. - */ - goto bail2; - } - -#endif - - if (i->parent_wsi) { - lwsl_info("%s: created child %p of parent %p\n", __func__, - wsi, i->parent_wsi); - wsi->parent = i->parent_wsi; - wsi->sibling_list = i->parent_wsi->child_list; - i->parent_wsi->child_list = wsi; - } -#ifdef LWS_WITH_HTTP_PROXY - if (i->uri_replace_to) - wsi->http.rw = lws_rewrite_create(wsi, html_parser_cb, - i->uri_replace_from, - i->uri_replace_to); -#endif - - return wsi; - -bail1: - lws_client_stash_destroy(wsi); - -bail: - lws_free(wsi); -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) -bail2: -#endif - if (i->pwsi) - *i->pwsi = NULL; - - return NULL; -} - struct lws * -lws_client_connect_via_info2(struct lws *wsi) +lws_http_client_connect_via_info2(struct lws *wsi) { struct client_info_stash *stash = wsi->stash; @@ -1156,55 +912,6 @@ bail1: return NULL; } -LWS_VISIBLE struct lws * -lws_client_connect_extended(struct lws_context *context, const char *address, - int port, int ssl_connection, const char *path, - const char *host, const char *origin, - const char *protocol, int ietf_version_or_minus_one, - void *userdata) -{ - struct lws_client_connect_info i; - - memset(&i, 0, sizeof(i)); - - i.context = context; - i.address = address; - i.port = port; - i.ssl_connection = ssl_connection; - i.path = path; - i.host = host; - i.origin = origin; - i.protocol = protocol; - i.ietf_version_or_minus_one = ietf_version_or_minus_one; - i.userdata = userdata; - - return lws_client_connect_via_info(&i); -} - -LWS_VISIBLE struct lws * -lws_client_connect(struct lws_context *context, const char *address, - int port, int ssl_connection, const char *path, - const char *host, const char *origin, - const char *protocol, int ietf_version_or_minus_one) -{ - struct lws_client_connect_info i; - - memset(&i, 0, sizeof(i)); - - i.context = context; - i.address = address; - i.port = port; - i.ssl_connection = ssl_connection; - i.path = path; - i.host = host; - i.origin = origin; - i.protocol = protocol; - i.ietf_version_or_minus_one = ietf_version_or_minus_one; - i.userdata = NULL; - - return lws_client_connect_via_info(&i); -} - #if defined(LWS_WITH_SOCKS5) void socks_generate_msg(struct lws *wsi, enum socks_msg_type type, ssize_t *msg_len) diff --git a/lib/roles/http/server/parsers.c b/lib/roles/http/server/parsers.c index cb022e362bef07aad1247bb455f7b3ff42483692..31851862e240bc96dc38c6eec33db7c204f1efa3 100644 --- a/lib/roles/http/server/parsers.c +++ b/lib/roles/http/server/parsers.c @@ -259,7 +259,7 @@ reset: #ifndef LWS_NO_CLIENT if (lwsi_role_client(wsi) && lwsi_state(wsi) == LRS_UNCONNECTED) - if (!lws_client_connect_via_info2(wsi)) + if (!lws_http_client_connect_via_info2(wsi)) /* our client connect has failed, the wsi * has been closed */ @@ -386,7 +386,7 @@ int __lws_header_table_detach(struct lws *wsi, int autoservice) if (lwsi_role_client(wsi) && lwsi_state(wsi) == LRS_UNCONNECTED) { lws_pt_unlock(pt); - if (!lws_client_connect_via_info2(wsi)) { + if (!lws_http_client_connect_via_info2(wsi)) { /* our client connect has failed, the wsi * has been closed */ diff --git a/lib/roles/http/server/server.c b/lib/roles/http/server/server.c index 350af3cd7eaf3b979225d4e186aeddab5cb0aafe..c5e0b88a9cddc2a8a98eeb1a39cd77cd5d38b43c 100644 --- a/lib/roles/http/server/server.c +++ b/lib/roles/http/server/server.c @@ -1630,85 +1630,6 @@ bail_nuke_ah: } -static int -lws_get_idlest_tsi(struct lws_context *context) -{ - unsigned int lowest = ~0; - int n = 0, hit = -1; - - for (; n < context->count_threads; n++) { - if ((unsigned int)context->pt[n].fds_count != - context->fd_limit_per_thread - 1 && - (unsigned int)context->pt[n].fds_count < lowest) { - lowest = context->pt[n].fds_count; - hit = n; - } - } - - return hit; -} - -struct lws * -lws_create_new_server_wsi(struct lws_vhost *vhost, int fixed_tsi) -{ - struct lws *new_wsi; - int n = fixed_tsi; - - if (n < 0) - n = lws_get_idlest_tsi(vhost->context); - - if (n < 0) { - lwsl_err("no space for new conn\n"); - return NULL; - } - - new_wsi = lws_zalloc(sizeof(struct lws), "new server wsi"); - if (new_wsi == NULL) { - lwsl_err("Out of memory for new connection\n"); - return NULL; - } - - new_wsi->tsi = n; - lwsl_debug("new wsi %p joining vhost %s, tsi %d\n", new_wsi, - vhost->name, new_wsi->tsi); - - new_wsi->vhost = vhost; - new_wsi->context = vhost->context; - new_wsi->pending_timeout = NO_PENDING_TIMEOUT; - new_wsi->rxflow_change_to = LWS_RXFLOW_ALLOW; - - /* initialize the instance struct */ - - lwsi_set_state(new_wsi, LRS_UNCONNECTED); - new_wsi->hdr_parsing_completed = 0; - -#ifdef LWS_WITH_TLS - new_wsi->tls.use_ssl = LWS_SSL_ENABLED(vhost); -#endif - - /* - * these can only be set once the protocol is known - * we set an un-established connection's protocol pointer - * to the start of the supported list, so it can look - * for matching ones during the handshake - */ - new_wsi->protocol = vhost->protocols; - new_wsi->user_space = NULL; - new_wsi->desc.sockfd = LWS_SOCK_INVALID; - new_wsi->position_in_fds_table = LWS_NO_FDS_POS; - - vhost->context->count_wsi_allocated++; - - /* - * outermost create notification for wsi - * no user_space because no protocol selection - */ - vhost->protocols[0].callback(new_wsi, LWS_CALLBACK_WSI_CREATE, - NULL, NULL, 0); - - return new_wsi; -} - LWS_VISIBLE int LWS_WARN_UNUSED_RESULT lws_http_transaction_completed(struct lws *wsi) { @@ -1821,325 +1742,6 @@ lws_http_transaction_completed(struct lws *wsi) return 0; } -/* if not a socket, it's a raw, non-ssl file descriptor */ - -LWS_VISIBLE struct lws * -lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type, - lws_sock_file_fd_type fd, const char *vh_prot_name, - struct lws *parent) -{ - struct lws_context *context = vh->context; - struct lws *new_wsi; - struct lws_context_per_thread *pt; - int n, ssl = 0; - -#if defined(LWS_WITH_PEER_LIMITS) - struct lws_peer *peer = NULL; - - if (type & LWS_ADOPT_SOCKET && !(type & LWS_ADOPT_WS_PARENTIO)) { - peer = lws_get_or_create_peer(vh, fd.sockfd); - - if (peer && context->ip_limit_wsi && - peer->count_wsi >= context->ip_limit_wsi) { - lwsl_notice("Peer reached wsi limit %d\n", - context->ip_limit_wsi); - lws_stats_atomic_bump(context, &context->pt[0], - LWSSTATS_C_PEER_LIMIT_WSI_DENIED, 1); - return NULL; - } - } -#endif - - n = -1; - if (parent) - n = parent->tsi; - new_wsi = lws_create_new_server_wsi(vh, n); - if (!new_wsi) { - if (type & LWS_ADOPT_SOCKET && !(type & LWS_ADOPT_WS_PARENTIO)) - compatible_close(fd.sockfd); - return NULL; - } -#if defined(LWS_WITH_PEER_LIMITS) - if (peer) - lws_peer_add_wsi(context, peer, new_wsi); -#endif - pt = &context->pt[(int)new_wsi->tsi]; - lws_stats_atomic_bump(context, pt, LWSSTATS_C_CONNECTIONS, 1); - - if (parent) { - new_wsi->parent = parent; - new_wsi->sibling_list = parent->child_list; - parent->child_list = new_wsi; - - if (type & LWS_ADOPT_WS_PARENTIO) - new_wsi->parent_carries_io = 1; - } - - new_wsi->desc = fd; - - if (vh_prot_name) { - new_wsi->protocol = lws_vhost_name_to_protocol(new_wsi->vhost, - vh_prot_name); - if (!new_wsi->protocol) { - lwsl_err("Protocol %s not enabled on vhost %s\n", - vh_prot_name, new_wsi->vhost->name); - goto bail; - } - if (lws_ensure_user_space(new_wsi)) { - lwsl_notice("OOM trying to get user_space\n"); - goto bail; - } -#if defined(LWS_ROLE_WS) - if (type & LWS_ADOPT_WS_PARENTIO) { - new_wsi->desc.sockfd = LWS_SOCK_INVALID; - lwsl_debug("binding to %s\n", new_wsi->protocol->name); - lws_bind_protocol(new_wsi, new_wsi->protocol); - lws_role_transition(new_wsi, LWSIFR_SERVER, - LRS_ESTABLISHED, &role_ops_ws); - /* allocate the ws struct for the wsi */ - new_wsi->ws = lws_zalloc(sizeof(*new_wsi->ws), "ws struct"); - if (!new_wsi->ws) { - lwsl_notice("OOM\n"); - goto bail; - } - lws_server_init_wsi_for_ws(new_wsi); - - return new_wsi; - } -#endif - } else -#if defined(LWS_ROLE_H1) - if (type & LWS_ADOPT_HTTP) {/* he will transition later */ - new_wsi->protocol = - &vh->protocols[vh->default_protocol_index]; - new_wsi->role_ops = &role_ops_h1; - } - else -#endif - { /* this is the only time he will transition */ - lws_bind_protocol(new_wsi, - &vh->protocols[vh->raw_protocol_index]); - lws_role_transition(new_wsi, 0, LRS_ESTABLISHED, - &role_ops_raw_skt); - } - - if (type & LWS_ADOPT_SOCKET) { /* socket desc */ - lwsl_debug("%s: new wsi %p, sockfd %d\n", __func__, new_wsi, - (int)(lws_intptr_t)fd.sockfd); -#if !defined(LWS_WITH_ESP32) - if (type & LWS_ADOPT_FLAG_UDP) - /* - * these can be >128 bytes, so just alloc for UDP - */ - new_wsi->udp = lws_malloc(sizeof(*new_wsi->udp), - "udp struct"); -#endif - - if (type & LWS_ADOPT_HTTP) - /* the transport is accepted... - * give him time to negotiate */ - lws_set_timeout(new_wsi, - PENDING_TIMEOUT_ESTABLISH_WITH_SERVER, - context->timeout_secs); - - } else /* file desc */ - lwsl_debug("%s: new wsi %p, filefd %d\n", __func__, new_wsi, - (int)(lws_intptr_t)fd.filefd); - - /* - * 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 the vhosts's default protocol, - * itself by default protocols[0] - */ - n = LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED; - if (!(type & LWS_ADOPT_HTTP)) { - if (!(type & LWS_ADOPT_SOCKET)) - n = LWS_CALLBACK_RAW_ADOPT_FILE; - else - n = LWS_CALLBACK_RAW_ADOPT; - } - - if (!LWS_SSL_ENABLED(new_wsi->vhost) || !(type & LWS_ADOPT_ALLOW_SSL) || - !(type & LWS_ADOPT_SOCKET)) { - /* non-SSL */ - if (!(type & LWS_ADOPT_HTTP)) { - if (!(type & LWS_ADOPT_SOCKET)) - lws_role_transition(new_wsi, 0, LRS_ESTABLISHED, - &role_ops_raw_file); - else - lws_role_transition(new_wsi, 0, LRS_ESTABLISHED, - &role_ops_raw_skt); - } -#if defined(LWS_ROLE_H1) - else - lws_role_transition(new_wsi, LWSIFR_SERVER, - LRS_HEADERS, &role_ops_h1); -#endif - } else { - /* SSL */ - if (!(type & LWS_ADOPT_HTTP)) - lws_role_transition(new_wsi, 0, LRS_SSL_INIT, - &role_ops_raw_skt); -#if defined(LWS_ROLE_H1) - else - lws_role_transition(new_wsi, LWSIFR_SERVER, - LRS_SSL_INIT, &role_ops_h1); -#endif - ssl = 1; - } - - lwsl_debug("new wsi wsistate 0x%x\n", new_wsi->wsistate); - - if (context->event_loop_ops->accept) - context->event_loop_ops->accept(new_wsi); - - if (!ssl) { - lws_pt_lock(pt, __func__); - if (__insert_wsi_socket_into_fds(context, new_wsi)) { - lws_pt_unlock(pt); - lwsl_err("%s: fail inserting socket\n", __func__); - goto fail; - } - lws_pt_unlock(pt); - } else - if (lws_server_socket_service_ssl(new_wsi, fd.sockfd)) { - lwsl_info("%s: fail ssl negotiation\n", __func__); - goto fail; - } - - /* - * by deferring callback to this point, after insertion to fds, - * lws_callback_on_writable() can work from the callback - */ - if ((new_wsi->protocol->callback)( - new_wsi, n, new_wsi->user_space, NULL, 0)) - goto fail; - - if (type & LWS_ADOPT_HTTP) { - if (!lws_header_table_attach(new_wsi, 0)) - lwsl_debug("Attached ah immediately\n"); - else - lwsl_info("%s: waiting for ah\n", __func__); - } - - lws_cancel_service_pt(new_wsi); - - return new_wsi; - -fail: - if (type & LWS_ADOPT_SOCKET) - lws_close_free_wsi(new_wsi, LWS_CLOSE_STATUS_NOSTATUS, "adopt skt fail"); - - return NULL; - -bail: - lwsl_notice("%s: exiting on bail\n", __func__); - if (parent) - parent->child_list = new_wsi->sibling_list; - if (new_wsi->user_space) - lws_free(new_wsi->user_space); - lws_free(new_wsi); - compatible_close(fd.sockfd); - - return NULL; -} - -LWS_VISIBLE struct lws * -lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd) -{ - lws_sock_file_fd_type fd; - - fd.sockfd = accept_fd; - return lws_adopt_descriptor_vhost(vh, LWS_ADOPT_SOCKET | - LWS_ADOPT_HTTP | LWS_ADOPT_ALLOW_SSL, fd, NULL, NULL); -} - -LWS_VISIBLE struct lws * -lws_adopt_socket(struct lws_context *context, lws_sockfd_type accept_fd) -{ - return lws_adopt_socket_vhost(context->vhost_list, accept_fd); -} - -/* Common read-buffer adoption for lws_adopt_*_readbuf */ -static struct lws* -adopt_socket_readbuf(struct lws *wsi, const char *readbuf, size_t len) -{ - struct lws_context_per_thread *pt; - struct lws_pollfd *pfd; - int n; - - if (!wsi) - return NULL; - - if (!readbuf || len == 0) - return wsi; - - if (wsi->position_in_fds_table == LWS_NO_FDS_POS) - return wsi; - - pt = &wsi->context->pt[(int)wsi->tsi]; - - n = lws_buflist_append_segment(&wsi->buflist, (const uint8_t *)readbuf, len); - if (n < 0) - goto bail; - if (n) - lws_dll_lws_add_front(&wsi->dll_buflist, &pt->dll_head_buflist); - - /* - * we can't process the initial read data until we can attach an ah. - * - * if one is available, get it and place the data in his ah rxbuf... - * wsi with ah that have pending rxbuf get auto-POLLIN service. - * - * no autoservice because we didn't get a chance to attach the - * readbuf data to wsi or ah yet, and we will do it next if we get - * the ah. - */ - if (wsi->http.ah || !lws_header_table_attach(wsi, 0)) { - - lwsl_notice("%s: calling service on readbuf ah\n", __func__); - - /* unlike a normal connect, we have the headers already - * (or the first part of them anyway). - * libuv won't come back and service us without a network - * event, so we need to do the header service right here. - */ - pfd = &pt->fds[wsi->position_in_fds_table]; - pfd->revents |= LWS_POLLIN; - lwsl_err("%s: calling service\n", __func__); - if (lws_service_fd_tsi(wsi->context, pfd, wsi->tsi)) - /* service closed us */ - return NULL; - - return wsi; - } - lwsl_err("%s: deferring handling ah\n", __func__); - - return wsi; - -bail: - lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "adopt skt readbuf fail"); - - return NULL; -} - -LWS_VISIBLE struct lws * -lws_adopt_socket_readbuf(struct lws_context *context, lws_sockfd_type accept_fd, - const char *readbuf, size_t len) -{ - return adopt_socket_readbuf(lws_adopt_socket(context, accept_fd), - readbuf, len); -} - -LWS_VISIBLE struct lws * -lws_adopt_socket_vhost_readbuf(struct lws_vhost *vhost, - lws_sockfd_type accept_fd, - const char *readbuf, size_t len) -{ - return adopt_socket_readbuf(lws_adopt_socket_vhost(vhost, accept_fd), - readbuf, len); -} LWS_VISIBLE int lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type, diff --git a/lib/roles/listen/ops-listen.c b/lib/roles/listen/ops-listen.c index dbeb9753a29150598a5d90e074655fefb4fd7be3..2aaee329d075b52ac35685006838b4dd886adbcd 100644 --- a/lib/roles/listen/ops-listen.c +++ b/lib/roles/listen/ops-listen.c @@ -171,6 +171,8 @@ struct lws_role_ops role_ops_listen = { /* close_role */ NULL, /* close_kill_connection */ NULL, /* destroy_role */ NULL, + /* adoption_bind */ NULL, + /* client_bind */ NULL, /* writeable cb clnt, srv */ { 0, 0 }, /* close cb clnt, srv */ { 0, 0 }, /* file_handle */ 0, diff --git a/lib/roles/pipe/ops-pipe.c b/lib/roles/pipe/ops-pipe.c index b9348d58d7238a41e3962ec08f0a566962d1e87b..96b06e7fd9e0e49d77f4eebbf4abd9e2fc92d373 100644 --- a/lib/roles/pipe/ops-pipe.c +++ b/lib/roles/pipe/ops-pipe.c @@ -75,6 +75,8 @@ struct lws_role_ops role_ops_pipe = { /* close_role */ NULL, /* close_kill_connection */ NULL, /* destroy_role */ NULL, + /* adoption_bind */ NULL, + /* client_bind */ NULL, /* writeable cb clnt, srv */ { 0, 0 }, /* close cb clnt, srv */ { 0, 0 }, /* file_handle */ 1, diff --git a/lib/roles/private.h b/lib/roles/private.h index ae4278b5d35fa6f45be88ffbde280c94f29fe6cd..7729b0b394839abee6a49e149c898183e3f72e4a 100644 --- a/lib/roles/private.h +++ b/lib/roles/private.h @@ -148,9 +148,11 @@ enum lwsi_state { }; #define lwsi_state(wsi) ((enum lwsi_state)(wsi->wsistate & LRS_MASK)) -#define lwsi_state_PRE_CLOSE(wsi) ((enum lwsi_state)(wsi->wsistate_pre_close & LRS_MASK)) +#define lwsi_state_PRE_CLOSE(wsi) \ + ((enum lwsi_state)(wsi->wsistate_pre_close & LRS_MASK)) #define lwsi_state_est(wsi) (!(wsi->wsistate & LWSIFS_NOT_EST)) -#define lwsi_state_est_PRE_CLOSE(wsi) (!(wsi->wsistate_pre_close & LWSIFS_NOT_EST)) +#define lwsi_state_est_PRE_CLOSE(wsi) \ + (!(wsi->wsistate_pre_close & LWSIFS_NOT_EST)) #define lwsi_state_can_handle_POLLOUT(wsi) (wsi->wsistate & LWSIFS_POCB) #if !defined (_DEBUG) #define lwsi_set_state(wsi, lrs) wsi->wsistate = \ @@ -159,6 +161,8 @@ enum lwsi_state { void lwsi_set_state(struct lws *wsi, lws_wsi_state_t lrs); #endif +#define _LWS_ADOPT_FINISH (1 << 24) + /* * internal role-specific ops */ @@ -217,6 +221,16 @@ struct lws_role_ops { /* role-specific destructor */ int (*destroy_role)(struct lws *wsi); + /* role-specific socket-adopt */ + int (*adoption_bind)(struct lws *wsi, int type, const char *prot); + /* role-specific client-bind: + * ret 1 = bound, 0 = not bound, -1 = fail out + * i may be NULL, indicating client_bind is being called after + * a successful bind earlier, to finalize the binding. In that + * case ret 0 = OK, 1 = fail, wsi needs freeing, -1 = fail, wsi freed */ + int (*client_bind)(struct lws *wsi, + const struct lws_client_connect_info *i); + /* * the callback reasons for WRITEABLE for client, server * (just client applies if no concept of client or server) @@ -280,3 +294,6 @@ enum { LWS_UPG_RET_CONTINUE, LWS_UPG_RET_BAIL }; + +int +lws_role_call_adoption_bind(struct lws *wsi, int type, const char *prot); diff --git a/lib/roles/raw-file/ops-raw-file.c b/lib/roles/raw-file/ops-raw-file.c new file mode 100644 index 0000000000000000000000000000000000000000..bbe80341f09f38286f2c0a794d9ddbc98c19b6ee --- /dev/null +++ b/lib/roles/raw-file/ops-raw-file.c @@ -0,0 +1,104 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation: + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include <core/private.h> + +static int +rops_handle_POLLIN_raw_file(struct lws_context_per_thread *pt, struct lws *wsi, + struct lws_pollfd *pollfd) +{ + int n; + + if (pollfd->revents & LWS_POLLOUT) { + n = lws_callback_as_writeable(wsi); + if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { + lwsl_info("failed at set pollfd\n"); + return LWS_HPI_RET_WSI_ALREADY_DIED; + } + if (n) + return LWS_HPI_RET_PLEASE_CLOSE_ME; + } + + if (pollfd->revents & LWS_POLLIN) { + if (user_callback_handle_rxflow(wsi->protocol->callback, + wsi, LWS_CALLBACK_RAW_RX_FILE, + wsi->user_space, NULL, 0)) { + lwsl_debug("raw rx callback closed it\n"); + return LWS_HPI_RET_PLEASE_CLOSE_ME; + } + } + + if (pollfd->revents & LWS_POLLHUP) + return LWS_HPI_RET_PLEASE_CLOSE_ME; + + return LWS_HPI_RET_HANDLED; +} + +#if !defined(LWS_NO_SERVER) +static int +rops_adoption_bind_raw_file(struct lws *wsi, int type, const char *vh_prot_name) +{ + /* no socket or http: it can only be a raw file */ + if ((type & LWS_ADOPT_HTTP) || (type & LWS_ADOPT_SOCKET) || + (type & _LWS_ADOPT_FINISH)) + return 0; /* no match */ + + lws_role_transition(wsi, 0, LRS_ESTABLISHED, &role_ops_raw_file); + + if (!vh_prot_name) + wsi->protocol = &wsi->vhost->protocols[ + wsi->vhost->default_protocol_index]; + + return 1; /* bound */ +} +#endif + +struct lws_role_ops role_ops_raw_file = { + /* role name */ "raw-file", + /* alpn id */ NULL, + /* check_upgrades */ NULL, + /* init_context */ NULL, + /* init_vhost */ NULL, + /* destroy_vhost */ NULL, + /* periodic_checks */ NULL, + /* service_flag_pending */ NULL, + /* handle_POLLIN */ rops_handle_POLLIN_raw_file, + /* handle_POLLOUT */ NULL, + /* perform_user_POLLOUT */ NULL, + /* callback_on_writable */ NULL, + /* tx_credit */ NULL, + /* write_role_protocol */ NULL, + /* encapsulation_parent */ NULL, + /* alpn_negotiated */ NULL, + /* close_via_role_protocol */ NULL, + /* close_role */ NULL, + /* close_kill_connection */ NULL, + /* destroy_role */ NULL, +#if !defined(LWS_NO_SERVER) + /* adoption_bind */ rops_adoption_bind_raw_file, +#else + NULL, +#endif + /* client_bind */ NULL, + /* writeable cb clnt, srv */ { LWS_CALLBACK_RAW_WRITEABLE_FILE, 0 }, + /* close cb clnt, srv */ { LWS_CALLBACK_RAW_CLOSE_FILE, 0 }, + /* file_handle */ 1, +}; diff --git a/lib/roles/raw/ops-raw.c b/lib/roles/raw-skt/ops-raw-skt.c similarity index 74% rename from lib/roles/raw/ops-raw.c rename to lib/roles/raw-skt/ops-raw-skt.c index 68b52bded64bbb026ed8995c669b881bcaac87e1..2dbd3b2f65916150c2caefc8ad44a94f75485bcb 100644 --- a/lib/roles/raw/ops-raw.c +++ b/lib/roles/raw-skt/ops-raw-skt.c @@ -135,38 +135,61 @@ fail: return LWS_HPI_RET_WSI_ALREADY_DIED; } +#if !defined(LWS_NO_SERVER) +static int +rops_adoption_bind_raw_skt(struct lws *wsi, int type, const char *vh_prot_name) +{ + /* no http but socket... must be raw skt */ + if ((type & LWS_ADOPT_HTTP) || !(type & LWS_ADOPT_SOCKET) || + (type & _LWS_ADOPT_FINISH)) + return 0; /* no match */ + +#if !defined(LWS_WITH_ESP32) + if (type & LWS_ADOPT_FLAG_UDP) + /* + * these can be >128 bytes, so just alloc for UDP + */ + wsi->udp = lws_malloc(sizeof(*wsi->udp), "udp struct"); +#endif + if (!vh_prot_name) + lws_bind_protocol(wsi, wsi->protocol); + else + /* this is the only time he will transition */ + lws_bind_protocol(wsi, + &wsi->vhost->protocols[wsi->vhost->raw_protocol_index]); + + lws_role_transition(wsi, 0, type & LWS_ADOPT_ALLOW_SSL ? LRS_SSL_INIT : + LRS_ESTABLISHED, &role_ops_raw_skt); + + return 1; /* bound */ +} +#endif + +#if !defined(LWS_NO_CLIENT) static int -rops_handle_POLLIN_raw_file(struct lws_context_per_thread *pt, struct lws *wsi, - struct lws_pollfd *pollfd) +rops_client_bind_raw_skt(struct lws *wsi, + const struct lws_client_connect_info *i) { - int n; + if (!i) { - if (pollfd->revents & LWS_POLLOUT) { - n = lws_callback_as_writeable(wsi); - if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { - lwsl_info("failed at set pollfd\n"); - return LWS_HPI_RET_WSI_ALREADY_DIED; - } - if (n) - return LWS_HPI_RET_PLEASE_CLOSE_ME; - } + /* finalize */ - if (pollfd->revents & LWS_POLLIN) { - if (user_callback_handle_rxflow(wsi->protocol->callback, - wsi, LWS_CALLBACK_RAW_RX_FILE, - wsi->user_space, NULL, 0)) { - lwsl_debug("raw rx callback closed it\n"); - return LWS_HPI_RET_PLEASE_CLOSE_ME; - } + if (!wsi->user_space && wsi->stash->method) + if (lws_ensure_user_space(wsi)) + return 1; + + return 0; } - if (pollfd->revents & LWS_POLLHUP) - return LWS_HPI_RET_PLEASE_CLOSE_ME; + /* we are a fallback if nothing else matched */ - return LWS_HPI_RET_HANDLED; -} + lws_role_transition(wsi, LWSIFR_CLIENT, LRS_UNCONNECTED, + &role_ops_raw_skt); + return 1; /* matched */ +} +#endif struct lws_role_ops role_ops_raw_skt = { /* role name */ "raw-skt", @@ -189,35 +212,17 @@ struct lws_role_ops role_ops_raw_skt = { /* close_role */ NULL, /* close_kill_connection */ NULL, /* destroy_role */ NULL, +#if !defined(LWS_NO_SERVER) + /* adoption_bind */ rops_adoption_bind_raw_skt, +#else + NULL, +#endif +#if !defined(LWS_NO_CLIENT) + /* client_bind */ rops_client_bind_raw_skt, +#else + NULL, +#endif /* writeable cb clnt, srv */ { LWS_CALLBACK_RAW_WRITEABLE, 0 }, /* close cb clnt, srv */ { LWS_CALLBACK_RAW_CLOSE, 0 }, /* file_handle */ 0, }; - - - -struct lws_role_ops role_ops_raw_file = { - /* role name */ "raw-file", - /* alpn id */ NULL, - /* check_upgrades */ NULL, - /* init_context */ NULL, - /* init_vhost */ NULL, - /* destroy_vhost */ NULL, - /* periodic_checks */ NULL, - /* service_flag_pending */ NULL, - /* handle_POLLIN */ rops_handle_POLLIN_raw_file, - /* handle_POLLOUT */ NULL, - /* perform_user_POLLOUT */ NULL, - /* callback_on_writable */ NULL, - /* tx_credit */ NULL, - /* write_role_protocol */ NULL, - /* encapsulation_parent */ NULL, - /* alpn_negotiated */ NULL, - /* close_via_role_protocol */ NULL, - /* close_role */ NULL, - /* close_kill_connection */ NULL, - /* destroy_role */ NULL, - /* writeable cb clnt, srv */ { LWS_CALLBACK_RAW_WRITEABLE_FILE, 0 }, - /* close cb clnt, srv */ { LWS_CALLBACK_RAW_CLOSE_FILE, 0 }, - /* file_handle */ 1, -}; diff --git a/lib/roles/ws/client-ws.c b/lib/roles/ws/client-ws.c index fd6cf4255129c24e9d20617349b983121332ae07..ad2d6f5dc92d62a3e6a7881379b7f6d40f0b91e0 100644 --- a/lib/roles/ws/client-ws.c +++ b/lib/roles/ws/client-ws.c @@ -40,7 +40,8 @@ strtolower(char *s) } int -lws_create_client_ws_object(struct lws_client_connect_info *i, struct lws *wsi) +lws_create_client_ws_object(const struct lws_client_connect_info *i, + struct lws *wsi) { int v = SPEC_LATEST_SUPPORTED; diff --git a/lib/roles/ws/ops-ws.c b/lib/roles/ws/ops-ws.c index 5ddaba9e18c1d9fb4407c527bc3c87732704593a..c9a353e82bee0973dfe30b7a69cb0ec35f2eb1d6 100644 --- a/lib/roles/ws/ops-ws.c +++ b/lib/roles/ws/ops-ws.c @@ -781,8 +781,7 @@ lws_server_init_wsi_for_ws(struct lws *wsi) lwsl_debug("Allocating RX buffer %d\n", n); #if !defined(LWS_WITH_ESP32) - if (!wsi->parent_carries_io && - !wsi->h2_stream_carries_ws) + if (!wsi->h2_stream_carries_ws) if (setsockopt(wsi->desc.sockfd, SOL_SOCKET, SO_SNDBUF, (const char *)&n, sizeof n)) { lwsl_warn("Failed to set SNDBUF to %d", n); @@ -1984,6 +1983,8 @@ struct lws_role_ops role_ops_ws = { /* close_role */ rops_close_role_ws, /* close_kill_connection */ rops_close_kill_connection_ws, /* destroy_role */ rops_destroy_role_ws, + /* adoption_bind */ NULL, + /* client_bind */ NULL, /* writeable cb clnt, srv */ { LWS_CALLBACK_CLIENT_WRITEABLE, LWS_CALLBACK_SERVER_WRITEABLE }, /* close cb clnt, srv */ { LWS_CALLBACK_CLIENT_CLOSED,