LWS_VISIBLE int lws_server_socket_service(struct lws_context *context, struct lws *wsi, struct lws_pollfd *pollfd) { struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; lws_sockfd_type accept_fd = LWS_SOCK_INVALID; struct allocated_headers *ah; #if LWS_POSIX struct sockaddr_in cli_addr; socklen_t clilen; #endif int n, len; switch (wsi->mode) { case LWSCM_HTTP_SERVING: case LWSCM_HTTP_SERVING_ACCEPTED: case LWSCM_HTTP2_SERVING: /* handle http headers coming in */ /* pending truncated sends have uber priority */ if (wsi->trunc_len) { if (!(pollfd->revents & LWS_POLLOUT)) break; if (lws_issue_raw(wsi, wsi->trunc_alloc + wsi->trunc_offset, wsi->trunc_len) < 0) goto fail; /* * we can't afford to allow input processing to send * something new, so spin around he event loop until * he doesn't have any partials */ break; } /* any incoming data ready? */ if (!(pollfd->revents & pollfd->events & LWS_POLLIN)) goto try_pollout; /* these states imply we MUST have an ah attached */ if (wsi->state == LWSS_HTTP || wsi->state == LWSS_HTTP_ISSUING_FILE || wsi->state == LWSS_HTTP_HEADERS) { if (!wsi->u.hdr.ah) /* no autoservice beacuse we will do it next */ if (lws_header_table_attach(wsi, 0)) goto try_pollout; ah = wsi->u.hdr.ah; lwsl_debug("%s: %p: rxpos:%d rxlen:%d\n", __func__, wsi, ah->rxpos, ah->rxlen); /* if nothing in ah rx buffer, get some fresh rx */ if (ah->rxpos == ah->rxlen) { ah->rxlen = lws_ssl_capable_read(wsi, ah->rx, sizeof(ah->rx)); ah->rxpos = 0; lwsl_debug("%s: wsi %p, ah->rxlen = %d\r\n", __func__, wsi, ah->rxlen); switch (ah->rxlen) { case 0: lwsl_info("%s: read 0 len\n", __func__); /* lwsl_info(" state=%d\n", wsi->state); */ // if (!wsi->hdr_parsing_completed) // lws_header_table_detach(wsi); /* fallthru */ case LWS_SSL_CAPABLE_ERROR: goto fail; case LWS_SSL_CAPABLE_MORE_SERVICE: ah->rxlen = ah->rxpos = 0; goto try_pollout; } } assert(ah->rxpos != ah->rxlen && ah->rxlen); /* just ignore incoming if waiting for close */ if (wsi->state != LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) { n = lws_read(wsi, ah->rx + ah->rxpos, ah->rxlen - ah->rxpos); if (n < 0) /* we closed wsi */ return 1; if (wsi->u.hdr.ah) { if ( wsi->u.hdr.ah->rxlen) wsi->u.hdr.ah->rxpos += n; if (wsi->u.hdr.ah->rxpos == wsi->u.hdr.ah->rxlen && (wsi->mode != LWSCM_HTTP_SERVING && wsi->mode != LWSCM_HTTP_SERVING_ACCEPTED && wsi->mode != LWSCM_HTTP2_SERVING)) lws_header_table_detach(wsi, 1); } break; } goto try_pollout; } len = lws_ssl_capable_read(wsi, pt->serv_buf, LWS_MAX_SOCKET_IO_BUF); lwsl_debug("%s: wsi %p read %d\r\n", __func__, wsi, len); switch (len) { case 0: lwsl_info("%s: read 0 len\n", __func__); /* lwsl_info(" state=%d\n", wsi->state); */ // if (!wsi->hdr_parsing_completed) // lws_header_table_detach(wsi); /* fallthru */ case LWS_SSL_CAPABLE_ERROR: goto fail; case LWS_SSL_CAPABLE_MORE_SERVICE: goto try_pollout; } /* just ignore incoming if waiting for close */ if (wsi->state != LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) { /* * hm this may want to send * (via HTTP callback for example) */ n = lws_read(wsi, pt->serv_buf, len); if (n < 0) /* we closed wsi */ return 1; /* hum he may have used up the * writability above */ break; } try_pollout: /* this handles POLLOUT for http serving fragments */ if (!(pollfd->revents & LWS_POLLOUT)) break; /* one shot */ if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) { lwsl_notice("%s a\n", __func__); goto fail; } if (!wsi->hdr_parsing_completed) break; if (wsi->state != LWSS_HTTP_ISSUING_FILE) { n = user_callback_handle_rxflow(wsi->protocol->callback, wsi, LWS_CALLBACK_HTTP_WRITEABLE, wsi->user_space, NULL, 0); if (n < 0) { lwsl_info("writeable_fail\n"); goto fail; } break; } /* >0 == completion, <0 == error */ n = lws_serve_http_file_fragment(wsi); if (n < 0 || (n > 0 && lws_http_transaction_completed(wsi))) { lwsl_info("completed\n"); goto fail; } break; case LWSCM_SERVER_LISTENER: #if LWS_POSIX /* pollin means a client has connected to us then */ do { if (!(pollfd->revents & LWS_POLLIN) || !(pollfd->events & LWS_POLLIN)) break; /* listen socket got an unencrypted connection... */ clilen = sizeof(cli_addr); lws_latency_pre(context, wsi); accept_fd = accept(pollfd->fd, (struct sockaddr *)&cli_addr, &clilen); lws_latency(context, wsi, "listener accept", accept_fd, accept_fd >= 0); if (accept_fd < 0) { if (LWS_ERRNO == LWS_EAGAIN || LWS_ERRNO == LWS_EWOULDBLOCK) { lwsl_err("accept asks to try again\n"); break; } lwsl_err("ERROR on accept: %s\n", strerror(LWS_ERRNO)); break; } lws_plat_set_socket_options(context, accept_fd); lwsl_debug("accepted new conn port %u on fd=%d\n", ntohs(cli_addr.sin_port), accept_fd); #else /* not very beautiful... */ accept_fd = (lws_sockfd_type)pollfd; #endif /* * look at who we connected to and give user code a chance * to reject based on client IP. There's no protocol selected * yet so we issue this to protocols[0] */ if ((context->protocols[0].callback)(wsi, LWS_CALLBACK_FILTER_NETWORK_CONNECTION, NULL, (void *)(long)accept_fd, 0)) { lwsl_debug("Callback denied network connection\n"); compatible_close(accept_fd); break; } if (!lws_adopt_socket(context, accept_fd)) /* already closed cleanly as necessary */ return 1; #if LWS_POSIX } while (pt->fds_count < context->fd_limit_per_thread - 1 && lws_poll_listen_fd(&pt->fds[wsi->position_in_fds_table]) > 0); #endif return 0; default: break; } if (!lws_server_socket_service_ssl(wsi, accept_fd)) return 0; fail: lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); return 1; }
static struct lws * lws_adopt_socket_vhost(struct lws_vhost *vh, lws_sockfd_type accept_fd) { struct lws_context *context = vh->context; struct lws *new_wsi = lws_create_new_server_wsi(vh); if (!new_wsi) { compatible_close(accept_fd); return NULL; } lwsl_info("%s: new wsi %p, sockfd %d\n", __func__, new_wsi, accept_fd); new_wsi->sock = accept_fd; /* the transport is accepted... give him time to negotiate */ lws_set_timeout(new_wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER, context->timeout_secs); #if LWS_POSIX == 0 mbed3_tcp_stream_accept(accept_fd, new_wsi); #endif /* * A new connection was accepted. Give the user a chance to * set properties of the newly created wsi. There's no protocol * selected yet so we issue this to protocols[0] */ if ((context->vhost_list->protocols[0].callback)(new_wsi, LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED, NULL, NULL, 0)) { compatible_close(new_wsi->sock); lws_free(new_wsi); return NULL; } lws_libev_accept(new_wsi, new_wsi->sock); lws_libuv_accept(new_wsi, new_wsi->sock); if (!LWS_SSL_ENABLED(new_wsi->vhost)) { if (insert_wsi_socket_into_fds(context, new_wsi)) { lwsl_err("%s: fail inserting socket\n", __func__); goto fail; } } else { new_wsi->mode = LWSCM_SSL_INIT; if (lws_server_socket_service_ssl(new_wsi, accept_fd)) { lwsl_err("%s: fail ssl negotiation\n", __func__); goto fail; } } if (!lws_header_table_attach(new_wsi, 0)) lwsl_debug("Attached ah immediately\n"); return new_wsi; fail: lws_close_free_wsi(new_wsi, LWS_CLOSE_STATUS_NOSTATUS); return NULL; }
LWS_VISIBLE int lws_server_socket_service(struct lws_context *context, struct lws *wsi, struct lws_pollfd *pollfd) { lws_sockfd_type accept_fd = LWS_SOCK_INVALID; struct lws_context_per_thread *pt = &context->pt[(int)wsi->tsi]; #if LWS_POSIX struct sockaddr_in cli_addr; socklen_t clilen; #endif struct lws *new_wsi = NULL; int n, len; switch (wsi->mode) { case LWSCM_HTTP_SERVING: case LWSCM_HTTP_SERVING_ACCEPTED: case LWSCM_HTTP2_SERVING: /* handle http headers coming in */ /* pending truncated sends have uber priority */ if (wsi->trunc_len) { if (!(pollfd->revents & LWS_POLLOUT)) break; if (lws_issue_raw(wsi, wsi->trunc_alloc + wsi->trunc_offset, wsi->trunc_len) < 0) goto fail; /* * we can't afford to allow input processing send * something new, so spin around he event loop until * he doesn't have any partials */ break; } /* any incoming data ready? */ if (!(pollfd->revents & pollfd->events && LWS_POLLIN)) goto try_pollout; len = lws_ssl_capable_read(wsi, pt->serv_buf, LWS_MAX_SOCKET_IO_BUF); lwsl_debug("%s: read %d\r\n", __func__, len); switch (len) { case 0: lwsl_info("lws_server_skt_srv: read 0 len\n"); /* lwsl_info(" state=%d\n", wsi->state); */ if (!wsi->hdr_parsing_completed) lws_free_header_table(wsi); /* fallthru */ case LWS_SSL_CAPABLE_ERROR: goto fail; case LWS_SSL_CAPABLE_MORE_SERVICE: goto try_pollout; } /* just ignore incoming if waiting for close */ if (wsi->state != LWSS_FLUSHING_STORED_SEND_BEFORE_CLOSE) { /* * hm this may want to send * (via HTTP callback for example) */ n = lws_read(wsi, pt->serv_buf, len); if (n < 0) /* we closed wsi */ return 1; /* hum he may have used up the * writability above */ break; } try_pollout: /* this handles POLLOUT for http serving fragments */ if (!(pollfd->revents & LWS_POLLOUT)) break; /* one shot */ if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) goto fail; lws_libev_io(wsi, LWS_EV_STOP | LWS_EV_WRITE); if (wsi->state != LWSS_HTTP_ISSUING_FILE) { n = user_callback_handle_rxflow( wsi->protocol->callback, wsi, LWS_CALLBACK_HTTP_WRITEABLE, wsi->user_space, NULL, 0); if (n < 0) goto fail; break; } /* >0 == completion, <0 == error */ n = lws_serve_http_file_fragment(wsi); if (n < 0 || (n > 0 && lws_http_transaction_completed(wsi))) goto fail; break; case LWSCM_SERVER_LISTENER: #if LWS_POSIX /* pollin means a client has connected to us then */ if (!(pollfd->revents & LWS_POLLIN)) break; /* listen socket got an unencrypted connection... */ clilen = sizeof(cli_addr); lws_latency_pre(context, wsi); accept_fd = accept(pollfd->fd, (struct sockaddr *)&cli_addr, &clilen); lws_latency(context, wsi, "unencrypted accept LWSCM_SERVER_LISTENER", accept_fd, accept_fd >= 0); if (accept_fd < 0) { if (LWS_ERRNO == LWS_EAGAIN || LWS_ERRNO == LWS_EWOULDBLOCK) { lwsl_debug("accept asks to try again\n"); break; } lwsl_warn("ERROR on accept: %s\n", strerror(LWS_ERRNO)); break; } lws_plat_set_socket_options(context, accept_fd); #else /* not very beautiful... */ accept_fd = (lws_sockfd_type)pollfd; #endif /* * look at who we connected to and give user code a chance * to reject based on client IP. There's no protocol selected * yet so we issue this to protocols[0] */ if ((context->protocols[0].callback)(wsi, LWS_CALLBACK_FILTER_NETWORK_CONNECTION, NULL, (void *)(long)accept_fd, 0)) { lwsl_debug("Callback denied network connection\n"); compatible_close(accept_fd); break; } new_wsi = lws_create_new_server_wsi(context); if (new_wsi == NULL) { compatible_close(accept_fd); break; } new_wsi->sock = accept_fd; new_wsi->tsi = lws_get_idlest_tsi(context); lwsl_info("Accepted to tsi %d\n", new_wsi->tsi); /* the transport is accepted... give him time to negotiate */ lws_set_timeout(new_wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER, AWAITING_TIMEOUT); #if LWS_POSIX == 0 mbed3_tcp_stream_accept(accept_fd, new_wsi); #endif /* * A new connection was accepted. Give the user a chance to * set properties of the newly created wsi. There's no protocol * selected yet so we issue this to protocols[0] */ (context->protocols[0].callback)(new_wsi, LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED, NULL, NULL, 0); lws_libev_accept(new_wsi, accept_fd); if (!LWS_SSL_ENABLED(context)) { #if LWS_POSIX lwsl_debug("accepted new conn port %u on fd=%d\n", ntohs(cli_addr.sin_port), accept_fd); #endif if (insert_wsi_socket_into_fds(context, new_wsi)) goto fail; } break; default: break; } if (!lws_server_socket_service_ssl(&wsi, new_wsi, accept_fd, pollfd)) return 0; fail: lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS); return 1; }
int lws_server_socket_service(struct libwebsocket_context *context, struct libwebsocket *wsi, struct libwebsocket_pollfd *pollfd) { struct libwebsocket *new_wsi = NULL; int accept_fd = 0; socklen_t clilen; struct sockaddr_in cli_addr; int n; int len; switch (wsi->mode) { case LWS_CONNMODE_HTTP_SERVING: case LWS_CONNMODE_HTTP_SERVING_ACCEPTED: /* handle http headers coming in */ /* pending truncated sends have uber priority */ if (wsi->truncated_send_malloc) { if (pollfd->revents & LWS_POLLOUT) if (lws_issue_raw(wsi, wsi->truncated_send_malloc + wsi->truncated_send_offset, wsi->truncated_send_len) < 0) { lwsl_info("closing from socket service\n"); return -1; } /* * we can't afford to allow input processing send * something new, so spin around he event loop until * he doesn't have any partials */ break; } /* any incoming data ready? */ if (pollfd->revents & LWS_POLLIN) { len = lws_ssl_capable_read(wsi, context->service_buffer, sizeof(context->service_buffer)); switch (len) { case 0: lwsl_info("lws_server_skt_srv: read 0 len\n"); /* lwsl_info(" state=%d\n", wsi->state); */ if (!wsi->hdr_parsing_completed) free(wsi->u.hdr.ah); /* fallthru */ case LWS_SSL_CAPABLE_ERROR: libwebsocket_close_and_free_session( context, wsi, LWS_CLOSE_STATUS_NOSTATUS); return 0; case LWS_SSL_CAPABLE_MORE_SERVICE: break; } /* just ignore incoming if waiting for close */ if (wsi->state != WSI_STATE_FLUSHING_STORED_SEND_BEFORE_CLOSE) { /* hm this may want to send (via HTTP callback for example) */ n = libwebsocket_read(context, wsi, context->service_buffer, len); if (n < 0) /* we closed wsi */ return 0; /* hum he may have used up the writability above */ break; } } /* this handles POLLOUT for http serving fragments */ if (!(pollfd->revents & LWS_POLLOUT)) break; /* one shot */ if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) goto fail; lws_libev_io(context, wsi, LWS_EV_STOP | LWS_EV_WRITE); if (wsi->state != WSI_STATE_HTTP_ISSUING_FILE) { n = user_callback_handle_rxflow( wsi->protocol->callback, wsi->protocol->owning_server, wsi, LWS_CALLBACK_HTTP_WRITEABLE, wsi->user_space, NULL, 0); if (n < 0) libwebsocket_close_and_free_session( context, wsi, LWS_CLOSE_STATUS_NOSTATUS); break; } /* nonzero for completion or error */ if (libwebsockets_serve_http_file_fragment(context, wsi)) libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); break; case LWS_CONNMODE_SERVER_LISTENER: /* pollin means a client has connected to us then */ if (!(pollfd->revents & LWS_POLLIN)) break; /* listen socket got an unencrypted connection... */ clilen = sizeof(cli_addr); lws_latency_pre(context, wsi); accept_fd = accept(pollfd->fd, (struct sockaddr *)&cli_addr, &clilen); lws_latency(context, wsi, "unencrypted accept LWS_CONNMODE_SERVER_LISTENER", accept_fd, accept_fd >= 0); if (accept_fd < 0) { if (LWS_ERRNO == LWS_EAGAIN || LWS_ERRNO == LWS_EWOULDBLOCK) { lwsl_debug("accept asks to try again\n"); break; } lwsl_warn("ERROR on accept: %s\n", strerror(LWS_ERRNO)); break; } lws_plat_set_socket_options(context, accept_fd); /* * look at who we connected to and give user code a chance * to reject based on client IP. There's no protocol selected * yet so we issue this to protocols[0] */ if ((context->protocols[0].callback)(context, wsi, LWS_CALLBACK_FILTER_NETWORK_CONNECTION, NULL, (void *)(long)accept_fd, 0)) { lwsl_debug("Callback denied network connection\n"); compatible_close(accept_fd); break; } new_wsi = libwebsocket_create_new_server_wsi(context); if (new_wsi == NULL) { compatible_close(accept_fd); break; } new_wsi->sock = accept_fd; /* the transport is accepted... give him time to negotiate */ libwebsocket_set_timeout(new_wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER, AWAITING_TIMEOUT); /* * A new connection was accepted. Give the user a chance to * set properties of the newly created wsi. There's no protocol * selected yet so we issue this to protocols[0] */ (context->protocols[0].callback)(context, new_wsi, LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED, NULL, NULL, 0); lws_libev_accept(context, new_wsi, accept_fd); if (!LWS_SSL_ENABLED(context)) { lwsl_debug("accepted new conn port %u on fd=%d\n", ntohs(cli_addr.sin_port), accept_fd); insert_wsi_socket_into_fds(context, new_wsi); } break; default: break; } if (new_wsi) if (lws_server_socket_service_ssl(context, &wsi, new_wsi, accept_fd, pollfd)) goto fail; return 0; fail: libwebsocket_close_and_free_session(context, wsi, LWS_CLOSE_STATUS_NOSTATUS); return 1; }