diff --git a/CMakeLists.txt b/CMakeLists.txt index 47eed5561..93d7210d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1608,7 +1608,10 @@ if ((LWS_ROLE_H1 OR LWS_ROLE_H2) AND NOT LWS_WITHOUT_TESTAPPS) endif() endif() - + if (LWS_WITH_LIBEV) + # libev generates a big mess of warnings with gcc, maintainer claims gcc to blame + set_source_files_properties( lib/event-libs/libev/libev.c PROPERTIES COMPILE_FLAGS "-Wno-error" ) + endif() if (NOT LWS_WITHOUT_SERVER) @@ -1651,9 +1654,8 @@ if ((LWS_ROLE_H1 OR LWS_ROLE_H2) AND NOT LWS_WITHOUT_TESTAPPS) "" "" "") - # libev generates a big mess of warnings with gcc, maintainers blame gcc + # libev generates a big mess of warnings with gcc, maintainer claims gcc to blame set_source_files_properties( test-apps/test-server-libev.c PROPERTIES COMPILE_FLAGS "-Wno-error" ) - set_source_files_properties( lib/event-libs/libev.c PROPERTIES COMPILE_FLAGS "-Wno-error" ) endif() if (NOT ((CMAKE_C_COMPILER_ID MATCHES "Clang") OR (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) @@ -1671,7 +1673,7 @@ if ((LWS_ROLE_H1 OR LWS_ROLE_H2) AND NOT LWS_WITHOUT_TESTAPPS) # # test-server-extpoll # - if (NOT LWS_WITHOUT_TEST_SERVER_EXTPOLL) + if (NOT LWS_WITHOUT_TEST_SERVER_EXTPOLL AND NOT WIN32) create_test_app(test-server-extpoll "test-apps/test-server.c" "" @@ -1889,7 +1891,7 @@ endif(LWS_WITH_GENERIC_SESSIONS AND LWS_ROLE_WS) endforeach() endif() endif() -endif(NOT LWS_WITHOUT_TESTAPPS) +endif((LWS_ROLE_H1 OR LWS_ROLE_H2) AND NOT LWS_WITHOUT_TESTAPPS) if (LWS_WITH_LWSWS) list(APPEND LWSWS_SRCS diff --git a/lib/context.c b/lib/context.c index 75bf45aac..d42f07c61 100644 --- a/lib/context.c +++ b/lib/context.c @@ -1794,10 +1794,8 @@ lws_context_destroy(struct lws_context *context) struct lws wsi; int n, m; - if (!context) { - lwsl_notice("%s: ctx %p\n", __func__, context); + if (!context) return; - } if (context->finalize_destroy_after_internal_loops_stopped) { if (context->event_loop_ops->destroy_context2) @@ -1809,8 +1807,11 @@ lws_context_destroy(struct lws_context *context) } if (context->being_destroyed1) { - if (!context->being_destroyed2) - return lws_context_destroy2(context); + if (!context->being_destroyed2) { + lws_context_destroy2(context); + + return; + } lwsl_info("%s: ctx %p: already being destroyed\n", __func__, context); return; diff --git a/lib/event-libs/README.md b/lib/event-libs/README.md index 3a7c377e1..c4c78a7e0 100644 --- a/lib/event-libs/README.md +++ b/lib/event-libs/README.md @@ -43,8 +43,6 @@ event libs, eg, ``` #if defined(LWS_WITH_LIBUV) #include "event-libs/libuv/private.h" -#else - #define LWS_LIBUV_ENABLED(context) (0) #endif ``` diff --git a/lib/event-libs/libev/libev.c b/lib/event-libs/libev/libev.c index 06e812b0e..746d57126 100644 --- a/lib/event-libs/libev/libev.c +++ b/lib/event-libs/libev/libev.c @@ -177,9 +177,6 @@ elops_init_context_ev(struct lws_context *context, static void elops_accept_ev(struct lws *wsi) { - struct lws_context *context = lws_get_context(wsi); - struct ev_io *r = &wsi->w_read.ev.watcher; - struct ev_io *w = &wsi->w_write.ev.watcher; int fd; if (wsi->role_ops->file_handle) @@ -187,10 +184,11 @@ elops_accept_ev(struct lws *wsi) else fd = wsi->desc.sockfd; - wsi->w_read.context = context; - wsi->w_write.context = context; - ev_io_init(r, lws_accept_cb, fd, EV_READ); - ev_io_init(w, lws_accept_cb, fd, EV_WRITE); + wsi->w_read.context = wsi->context; + wsi->w_write.context = wsi->context; + + ev_io_init(&wsi->w_read.ev.watcher, lws_accept_cb, fd, EV_READ); + ev_io_init(&wsi->w_write.ev.watcher, lws_accept_cb, fd, EV_WRITE); } static void diff --git a/lib/event-libs/libev/private.h b/lib/event-libs/libev/private.h index 6ff8ba172..54e1b124e 100644 --- a/lib/event-libs/libev/private.h +++ b/lib/event-libs/libev/private.h @@ -25,6 +25,7 @@ struct lws_pt_eventlibs_libev { struct ev_loop *io_loop; + struct ev_timer hrtimer; }; struct lws_io_watcher_libev { diff --git a/lib/event-libs/libevent/libevent.c b/lib/event-libs/libevent/libevent.c index bd56ea97b..43dda05c5 100644 --- a/lib/event-libs/libevent/libevent.c +++ b/lib/event-libs/libevent/libevent.c @@ -1,7 +1,7 @@ /* * libwebsockets - small server side websockets and web server implementation * - * Copyright (C) 2010-2017 Andy Green + * Copyright (C) 2010-2018 Andy Green * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public diff --git a/lib/event-libs/libevent/private.h b/lib/event-libs/libevent/private.h index 2c4de3ea7..347c7fe54 100644 --- a/lib/event-libs/libevent/private.h +++ b/lib/event-libs/libevent/private.h @@ -25,6 +25,7 @@ struct lws_pt_eventlibs_libevent { struct event_base *io_loop; + struct event *hrtimer; }; struct lws_io_watcher_libevent { diff --git a/lib/event-libs/libuv/private.h b/lib/event-libs/libuv/private.h index c7b46bd8f..173340e35 100644 --- a/lib/event-libs/libuv/private.h +++ b/lib/event-libs/libuv/private.h @@ -62,3 +62,5 @@ struct lws_signal_watcher_libuv { extern struct lws_event_loop_ops event_loop_ops_uv; +LWS_VISIBLE uv_loop_t * +lws_uv_getloop(struct lws_context *context, int tsi); diff --git a/lib/event-libs/private.h b/lib/event-libs/private.h new file mode 100644 index 000000000..59705c9ec --- /dev/null +++ b/lib/event-libs/private.h @@ -0,0 +1,74 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2018 Andy Green + * + * 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 + * + * This is included from private-libwebsockets.h + */ + +struct lws_event_loop_ops { + const char *name; + /* event loop-specific context init during context creation */ + int (*init_context)(struct lws_context *context, + const struct lws_context_creation_info *info); + /* called during lws_destroy_context */ + int (*destroy_context1)(struct lws_context *context); + /* called during lws_destroy_context2 */ + int (*destroy_context2)(struct lws_context *context); + /* init vhost listening wsi */ + int (*init_vhost_listen_wsi)(struct lws *wsi); + /* init the event loop for a pt */ + int (*init_pt)(struct lws_context *context, void *_loop, int tsi); + /* called at end of first phase of close_free_wsi() */ + int (*wsi_logical_close)(struct lws *wsi); + /* return nonzero if client connect not allowed */ + int (*check_client_connect_ok)(struct lws *wsi); + /* close handle manually */ + void (*close_handle_manually)(struct lws *wsi); + /* event loop accept processing */ + void (*accept)(struct lws *wsi); + /* control wsi active events */ + void (*io)(struct lws *wsi, int flags); + /* run the event loop for a pt */ + void (*run_pt)(struct lws_context *context, int tsi); + /* called before pt is destroyed */ + void (*destroy_pt)(struct lws_context *context, int tsi); + /* called just before wsi is freed */ + void (*destroy_wsi)(struct lws *wsi); + + unsigned int periodic_events_available:1; +}; + +/* bring in event libs private declarations */ + +#if defined(LWS_WITH_POLL) +#include "event-libs/poll/private.h" +#endif + +#if defined(LWS_WITH_LIBUV) +#include "event-libs/libuv/private.h" +#endif + +#if defined(LWS_WITH_LIBEVENT) +#include "event-libs/libevent/private.h" +#endif + +#if defined(LWS_WITH_LIBEV) +#include "event-libs/libev/private.h" +#endif + diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c index 9f7ce8e2f..af4ca2f60 100644 --- a/lib/libwebsockets.c +++ b/lib/libwebsockets.c @@ -574,13 +574,14 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char * * must go through and close all those first */ if (wsi->vhost) { - lws_vhost_lock(wsi->vhost); + if ((int)reason != -1) + lws_vhost_lock(wsi->vhost); lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1, wsi->dll_client_transaction_queue_head.next) { struct lws *w = lws_container_of(d, struct lws, dll_client_transaction_queue); - __lws_close_free_wsi(w, reason, "trans q leader closing"); + __lws_close_free_wsi(w, -1, "trans q leader closing"); } lws_end_foreach_dll_safe(d, d1); /* @@ -592,7 +593,8 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char * * queue leader is closing. */ lws_dll_lws_remove(&wsi->dll_client_transaction_queue); - lws_vhost_unlock(wsi->vhost); + if ((int)reason !=-1) + lws_vhost_unlock(wsi->vhost); } #endif @@ -1030,6 +1032,7 @@ lws_buflist_use_segment(struct lws_buflist **head, size_t len) void lws_buflist_describe(struct lws_buflist **head, void *id) { + struct lws_buflist *old; int n = 0; if (*head == NULL) @@ -1040,7 +1043,12 @@ lws_buflist_describe(struct lws_buflist **head, void *id) (unsigned long long)(*head)->pos, (unsigned long long)(*head)->len, (unsigned long long)(*head)->len - (*head)->pos); + old = *head; head = &((*head)->next); + if (*head == old) { + lwsl_err("%s: next points to self\n", __func__); + break; + } n++; } } diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h index d97ca4a96..3ccb4a725 100644 --- a/lib/libwebsockets.h +++ b/lib/libwebsockets.h @@ -1,7 +1,7 @@ /* * libwebsockets - small server side websockets and web server implementation * - * Copyright (C) 2010-2017 Andy Green + * Copyright (C) 2010-2018 Andy Green * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -4606,7 +4606,7 @@ lws_plat_recommended_rsa_bits(void); * cleanly and at the moment all of its libuv objects have completed close. */ -LWS_VISIBLE uv_loop_t * +LWS_VISIBLE LWS_EXTERN uv_loop_t * lws_uv_getloop(struct lws_context *context, int tsi); LWS_VISIBLE LWS_EXTERN void diff --git a/lib/plat/lws-plat-unix.c b/lib/plat/lws-plat-unix.c index 80b43b1b0..8ace47586 100644 --- a/lib/plat/lws-plat-unix.c +++ b/lib/plat/lws-plat-unix.c @@ -57,8 +57,6 @@ lws_plat_pipe_signal(struct lws *wsi) n = write(pt->dummy_pipe_fds[1], &buf, 1); - lwsl_debug("%s: fd %d %d\n", __func__, pt->dummy_pipe_fds[1], n); - return n != 1; } diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h index 2aafdeb12..d731c183b 100644 --- a/lib/private-libwebsockets.h +++ b/lib/private-libwebsockets.h @@ -374,210 +374,11 @@ enum lws_ssl_capable_status { /* * - * ------ role ------ + * ------ roles ------ * */ -typedef uint32_t lws_wsi_state_t; - -/* - * The wsi->role_ops pointer decides almost everything about what role the wsi - * will play, h2, raw, ws, etc. - * - * However there are a few additional flags needed that vary, such as if the - * role is a client or server side, if it has that concept. And the connection - * fulfilling the role, has a separate dynamic state. - * - * 31 16 15 0 - * [ role flags ] [ state ] - * - * The role flags part is generally invariant for the lifetime of the wsi, - * although it can change if the connection role itself does, eg, if the - * connection upgrades from H1 -> WS1 the role flags may be changed at that - * point. - * - * The state part reflects the dynamic connection state, and the states are - * reused between roles. - * - * None of the internal role or state representations are made available outside - * of lws internals. Even for lws internals, if you add stuff here, please keep - * the constants inside this header only by adding necessary helpers here and - * use the helpers in the actual code. This is to ease any future refactors. - * - * Notice LWSIFR_ENCAP means we have a parent wsi that actually carries our - * data as a stream inside a different protocol. - */ - -#define _RS 16 - -#define LWSIFR_CLIENT (0x1000 << _RS) /* client side */ -#define LWSIFR_SERVER (0x2000 << _RS) /* server side */ - -#define LWSIFR_P_ENCAP_H2 (0x0100 << _RS) /* we are encapsulated by h2 */ - -enum lwsi_role { - LWSI_ROLE_MASK = (0xffff << _RS), - LWSI_ROLE_ENCAP_MASK = (0x0f00 << _RS), -}; - -#define lwsi_role(wsi) (wsi->wsistate & LWSI_ROLE_MASK) -#if !defined (_DEBUG) -#define lwsi_set_role(wsi, role) wsi->wsistate = \ - (wsi->wsistate & (~LWSI_ROLE_MASK)) | role -#else -void lwsi_set_role(struct lws *wsi, lws_wsi_state_t role); -#endif - -#define lwsi_role_client(wsi) (!!(wsi->wsistate & LWSIFR_CLIENT)) -#define lwsi_role_server(wsi) (!!(wsi->wsistate & LWSIFR_SERVER)) -#define lwsi_role_h2_ENCAPSULATION(wsi) \ - ((wsi->wsistate & LWSI_ROLE_ENCAP_MASK) == LWSIFR_P_ENCAP_H2) - -/* Pollout wants a callback in this state */ -#define LWSIFS_POCB (0x100) -/* Before any protocol connection was established */ -#define LWSIFS_NOT_EST (0x200) - -enum lwsi_state { - - /* Phase 1: pre-transport */ - - LRS_UNCONNECTED = LWSIFS_NOT_EST | 0, - LRS_WAITING_CONNECT = LWSIFS_NOT_EST | 1, - - /* Phase 2: establishing intermediaries on top of transport */ - - LRS_WAITING_PROXY_REPLY = LWSIFS_NOT_EST | 2, - LRS_WAITING_SSL = LWSIFS_NOT_EST | 3, - LRS_WAITING_SOCKS_GREETING_REPLY = LWSIFS_NOT_EST | 4, - LRS_WAITING_SOCKS_CONNECT_REPLY = LWSIFS_NOT_EST | 5, - LRS_WAITING_SOCKS_AUTH_REPLY = LWSIFS_NOT_EST | 6, - - /* Phase 3: establishing tls tunnel */ - - LRS_SSL_INIT = LWSIFS_NOT_EST | 7, - LRS_SSL_ACK_PENDING = LWSIFS_NOT_EST | 8, - LRS_PRE_WS_SERVING_ACCEPT = LWSIFS_NOT_EST | 9, - - /* Phase 4: connected */ - - LRS_WAITING_SERVER_REPLY = LWSIFS_NOT_EST | 10, - LRS_H2_AWAIT_PREFACE = LWSIFS_NOT_EST | 11, - LRS_H2_AWAIT_SETTINGS = LWSIFS_NOT_EST | - LWSIFS_POCB | 12, - - /* Phase 5: protocol logically established */ - - LRS_H2_CLIENT_SEND_SETTINGS = LWSIFS_POCB | 13, - LRS_H2_WAITING_TO_SEND_HEADERS = LWSIFS_POCB | 14, - LRS_DEFERRING_ACTION = LWSIFS_POCB | 15, - LRS_IDLING = 16, - LRS_H1C_ISSUE_HANDSHAKE = 17, - LRS_H1C_ISSUE_HANDSHAKE2 = 18, - LRS_ISSUE_HTTP_BODY = 19, - LRS_ISSUING_FILE = 20, - LRS_HEADERS = 21, - LRS_BODY = 22, - LRS_ESTABLISHED = LWSIFS_POCB | 23, - - /* Phase 6: finishing */ - - LRS_WAITING_TO_SEND_CLOSE = LWSIFS_POCB | 24, - LRS_RETURNED_CLOSE = LWSIFS_POCB | 25, - LRS_AWAITING_CLOSE_ACK = LWSIFS_POCB | 26, - LRS_FLUSHING_BEFORE_CLOSE = LWSIFS_POCB | 27, - LRS_SHUTDOWN = 28, - - /* Phase 7: dead */ - - LRS_DEAD_SOCKET = 29, - - LRS_MASK = 0xffff -}; - -#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_est(wsi) (!(wsi->wsistate & 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 = \ - (wsi->wsistate & (~LRS_MASK)) | lrs -#else -void lwsi_set_state(struct lws *wsi, lws_wsi_state_t lrs); -#endif - -/* - * internal role-specific ops - */ -struct lws_context_per_thread; -struct lws_role_ops { - const char *name; - const char *alpn; - /* - * After http headers have parsed, this is the last chance for a role - * to upgrade the connection to something else using the headers. - * ws-over-h2 is upgraded from h2 like this. - */ - int (*check_upgrades)(struct lws *wsi); - /* role-specific context init during context creation */ - int (*init_context)(struct lws_context *context, - const struct lws_context_creation_info *info); - /* role-specific per-vhost init during vhost creation */ - int (*init_vhost)(struct lws_vhost *vh, - const struct lws_context_creation_info *info); - /* role-specific per-vhost destructor during vhost destroy */ - int (*destroy_vhost)(struct lws_vhost *vh); - /* generic 1Hz callback for the role itself */ - int (*periodic_checks)(struct lws_context *context, int tsi, - time_t now); - /* chance for the role to force POLLIN without network activity */ - int (*service_flag_pending)(struct lws_context *context, int tsi); - /* an fd using this role has POLLIN signalled */ - int (*handle_POLLIN)(struct lws_context_per_thread *pt, struct lws *wsi, - struct lws_pollfd *pollfd); - /* an fd using the role wanted a POLLOUT callback and now has it */ - int (*handle_POLLOUT)(struct lws *wsi); - /* perform user pollout */ - int (*perform_user_POLLOUT)(struct lws *wsi); - /* do effective callback on writeable */ - int (*callback_on_writable)(struct lws *wsi); - /* connection-specific tx credit in bytes */ - lws_fileofs_t (*tx_credit)(struct lws *wsi); - /* role-specific write formatting */ - int (*write_role_protocol)(struct lws *wsi, unsigned char *buf, - size_t len, enum lws_write_protocol *wp); - - /* get encapsulation parent */ - struct lws * (*encapsulation_parent)(struct lws *wsi); - - /* role-specific destructor */ - int (*alpn_negotiated)(struct lws *wsi, const char *alpn); - - /* chance for the role to handle close in the protocol */ - int (*close_via_role_protocol)(struct lws *wsi, - enum lws_close_status reason); - /* role-specific close processing */ - int (*close_role)(struct lws_context_per_thread *pt, struct lws *wsi); - /* role-specific connection close processing */ - int (*close_kill_connection)(struct lws *wsi, - enum lws_close_status reason); - /* role-specific destructor */ - int (*destroy_role)(struct lws *wsi); - - /* - * the callback reasons for WRITEABLE for client, server - * (just client applies if no concept of client or server) - */ - uint16_t writeable_cb[2]; - /* - * the callback reasons for CLOSE for client, server - * (just client applies if no concept of client or server) - */ - uint16_t close_cb[2]; - - unsigned int file_handle:1; /* role operates on files not sockets */ -}; +#include "roles/private.h" /* null-terminated array of pointers to roles lws built with */ extern const struct lws_role_ops *available_roles[]; @@ -589,113 +390,13 @@ extern const struct lws_role_ops *available_roles[]; #define LWS_FOR_EVERY_AVAILABLE_ROLE_END }} -/* core roles */ -extern struct lws_role_ops role_ops_raw_skt, role_ops_raw_file, role_ops_listen, - role_ops_pipe; - -/* bring in role private declarations */ - -#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) - #include "roles/http/private.h" -#else - #define lwsi_role_http(wsi) (0) -#endif - -#if defined(LWS_ROLE_H1) - #include "roles/h1/private.h" -#else - #define lwsi_role_h1(wsi) (0) -#endif - -#if defined(LWS_ROLE_H2) - #include "roles/h2/private.h" -#else - #define lwsi_role_h2(wsi) (0) -#endif - -#if defined(LWS_ROLE_WS) - #include "roles/ws/private.h" -#else - #define lwsi_role_ws(wsi) (0) -#endif - -#if defined(LWS_ROLE_CGI) - #include "roles/cgi/private.h" -#else - #define lwsi_role_cgi(wsi) (0) -#endif - -enum { - LWS_HP_RET_BAIL_OK, - LWS_HP_RET_BAIL_DIE, - LWS_HP_RET_USER_SERVICE, - - LWS_HPI_RET_WSI_ALREADY_DIED, /* we closed it */ - LWS_HPI_RET_HANDLED, /* no probs */ - LWS_HPI_RET_PLEASE_CLOSE_ME, /* close it for us */ - - LWS_UPG_RET_DONE, - LWS_UPG_RET_CONTINUE, - LWS_UPG_RET_BAIL -}; - /* * * ------ event_loop ops ------ * */ -struct lws_event_loop_ops { - const char *name; - /* event loop-specific context init during context creation */ - int (*init_context)(struct lws_context *context, - const struct lws_context_creation_info *info); - /* called during lws_destroy_context */ - int (*destroy_context1)(struct lws_context *context); - /* called during lws_destroy_context2 */ - int (*destroy_context2)(struct lws_context *context); - /* init vhost listening wsi */ - int (*init_vhost_listen_wsi)(struct lws *wsi); - /* init the event loop for a pt */ - int (*init_pt)(struct lws_context *context, void *_loop, int tsi); - /* called at end of first phase of close_free_wsi() */ - int (*wsi_logical_close)(struct lws *wsi); - /* return nonzero if client connect not allowed */ - int (*check_client_connect_ok)(struct lws *wsi); - /* close handle manually */ - void (*close_handle_manually)(struct lws *wsi); - /* event loop accept processing */ - void (*accept)(struct lws *wsi); - /* control wsi active events */ - void (*io)(struct lws *wsi, int flags); - /* run the event loop for a pt */ - void (*run_pt)(struct lws_context *context, int tsi); - /* called before pt is destroyed */ - void (*destroy_pt)(struct lws_context *context, int tsi); - /* called just before wsi is freed */ - void (*destroy_wsi)(struct lws *wsi); - - unsigned int periodic_events_available:1; -}; - -/* bring in event libs private declarations */ - -#if defined(LWS_WITH_POLL) -#include "event-libs/poll/private.h" -#endif - -#if defined(LWS_WITH_LIBUV) -#include "event-libs/libuv/private.h" -#endif - -#if defined(LWS_WITH_LIBEVENT) -#include "event-libs/libevent/private.h" -#endif - -#if defined(LWS_WITH_LIBEV) -#include "event-libs/libev/private.h" -#endif - +#include "event-libs/private.h" /* enums of socks version */ enum socks_version { @@ -774,8 +475,6 @@ struct lws_ring { struct lws_protocols; struct lws; -#if defined(LWS_WITH_LIBEV) || defined(LWS_WITH_LIBUV) || defined(LWS_WITH_LIBEVENT) - struct lws_io_watcher { #ifdef LWS_WITH_LIBEV struct lws_io_watcher_libev ev; @@ -803,7 +502,6 @@ struct lws_signal_watcher { #endif struct lws_context *context; }; -#endif #ifdef _WIN32 #define LWS_FD_HASH(fd) ((fd ^ (fd >> 8) ^ (fd >> 16)) % FD_HASHTABLE_MODULUS) diff --git a/lib/roles/README.md b/lib/roles/README.md index 0e3a01c49..7e7126879 100644 --- a/lib/roles/README.md +++ b/lib/roles/README.md @@ -66,7 +66,7 @@ However when the declarations must be accessible to other things in lws build, e the role adds members to `struct lws` when enabled, they should be in the role directory in a file `private.h`. -Search for "bring in role private declarations" in `./lib/private-libwebsockets.h +Search for "bring in role private declarations" in `./lib/roles/private.h and add your private role file there following the style used for the other roles, eg, diff --git a/lib/roles/h1/ops-h1.c b/lib/roles/h1/ops-h1.c index 0cde99d5f..0f14264b2 100644 --- a/lib/roles/h1/ops-h1.c +++ b/lib/roles/h1/ops-h1.c @@ -314,11 +314,17 @@ lws_h1_server_socket_service(struct lws *wsi, struct lws_pollfd *pollfd) goto try_pollout; } + /* + * We got here because there was specifically POLLIN... + * regardless of our buflist state, we need to get it, + * and either use it, or append to the buflist and use + * buflist head material. + */ + buffered = lws_buflist_aware_read(pt, wsi, &ebuf); switch (ebuf.len) { case 0: - lwsl_info("%s: read 0 len a\n", - __func__); + lwsl_info("%s: read 0 len a\n", __func__); wsi->seen_zero_length_recv = 1; lws_change_pollfd(wsi, LWS_POLLIN, 0); goto try_pollout; diff --git a/lib/roles/h2/ops-h2.c b/lib/roles/h2/ops-h2.c index f10f7c93e..208342f4c 100644 --- a/lib/roles/h2/ops-h2.c +++ b/lib/roles/h2/ops-h2.c @@ -279,11 +279,19 @@ drain: lws_dll_lws_remove(&wsi->dll_buflist); } } else - if (n != ebuf.len && - lws_buflist_append_segment(&wsi->buflist, + if (n != ebuf.len) { + m = lws_buflist_append_segment(&wsi->buflist, (uint8_t *)ebuf.token + n, - ebuf.len - n) < 0) - return LWS_HPI_RET_PLEASE_CLOSE_ME; + ebuf.len - n); + if (m < 0) + return LWS_HPI_RET_PLEASE_CLOSE_ME; + if (m) { + lwsl_debug("%s: added %p to rxflow list\n", + __func__, wsi); + lws_dll_lws_add_front(&wsi->dll_buflist, + &pt->dll_head_buflist); + } + } } // lws_buflist_describe(&wsi->buflist, wsi); diff --git a/lib/roles/http/client/client-handshake.c b/lib/roles/http/client/client-handshake.c index 6455a1c04..c4b61bcb9 100644 --- a/lib/roles/http/client/client-handshake.c +++ b/lib/roles/http/client/client-handshake.c @@ -123,10 +123,11 @@ lws_client_connect_2(struct lws *wsi) } #endif - lwsl_info("applying %p to txn queue on %p (wsistate 0x%x)\n", wsi, w, - w->wsistate); + lwsl_info("applying %p to txn queue on %p (wsistate 0x%x)\n", + wsi, w, w->wsistate); /* * ...let's add ourselves to his transaction queue... + * we are adding ourselves at the HEAD */ lws_dll_lws_add_front(&wsi->dll_client_transaction_queue, &w->dll_client_transaction_queue_head); @@ -522,12 +523,15 @@ send_hs: * LRS_H1C_ISSUE_HANDSHAKE2, and let them write. * * If we are trying to do this too early, before the master - * connection has written his own headers, + * connection has written his own headers, then it will just + * wait in the queue until it's possible to send them. */ lws_callback_on_writable(wsi_piggyback); - lwsl_info("wsi %p: waiting to send headers\n", wsi); + lwsl_info("%s: wsi %p: waiting to send headers (parent state %x)\n", + __func__, wsi, lwsi_state(wsi_piggyback)); } else { - lwsl_info("wsi %p: client creating own connection\n", wsi); + lwsl_info("%s: wsi %p: client creating own connection\n", + __func__, wsi); /* we are making our own connection */ lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE); diff --git a/lib/roles/http/client/client.c b/lib/roles/http/client/client.c index 1a1a27c29..1a61e62c1 100644 --- a/lib/roles/http/client/client.c +++ b/lib/roles/http/client/client.c @@ -29,24 +29,26 @@ lws_client_http_body_pending(struct lws *wsi, int something_left_to_send) /* * return self, or queued client wsi we are acting on behalf of + * + * That is the TAIL of the queue (new queue elements are added at the HEAD) */ struct lws * lws_client_wsi_effective(struct lws *wsi) { - struct lws *wsi_eff = wsi; - struct lws_dll_lws *d; + struct lws_dll_lws *tail = NULL; if (!wsi->transaction_from_pipeline_queue || !wsi->dll_client_transaction_queue_head.next) return wsi; - d = wsi->dll_client_transaction_queue_head.next; - if (d) - wsi_eff = lws_container_of(d, struct lws, - dll_client_transaction_queue); + lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1, + wsi->dll_client_transaction_queue_head.next) { + tail = d; + } lws_end_foreach_dll_safe(d, d1); - return wsi_eff; + return lws_container_of(tail, struct lws, + dll_client_transaction_queue); } /* @@ -97,34 +99,40 @@ lws_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd, if ((pollfd->revents & LWS_POLLOUT) && wsi->keepalive_active && wsi->dll_client_transaction_queue_head.next) { - int found = 0; + struct lws *wfound = NULL; lwsl_debug("%s: pollout HANDSHAKE2\n", __func__); - /* we have a transaction queue that wants to pipeline */ + /* + * We have a transaction queued that wants to pipeline. + * + * We have to allow it to send headers strictly in the order + * that it was queued, ie, tail-first. + */ lws_vhost_lock(wsi->vhost); lws_start_foreach_dll_safe(struct lws_dll_lws *, d, d1, wsi->dll_client_transaction_queue_head.next) { struct lws *w = lws_container_of(d, struct lws, dll_client_transaction_queue); - lwsl_notice("%s: %p states 0x%x\n", __func__, w, w->wsistate); - if (lwsi_state(w) == LRS_H1C_ISSUE_HANDSHAKE2) { - /* - * pollfd has the master sockfd in it... we - * need to use that in HANDSHAKE2 to understand - * which wsi to actually write on - */ - lws_client_socket_service(w, pollfd, wsi); - lws_callback_on_writable(wsi); - found = 1; - break; - } + lwsl_debug("%s: %p states 0x%x\n", __func__, w, w->wsistate); + if (lwsi_state(w) == LRS_H1C_ISSUE_HANDSHAKE2) + wfound = w; } lws_end_foreach_dll_safe(d, d1); - lws_vhost_unlock(wsi->vhost); - if (!found) - lwsl_err("%s: didn't find anything in HS2\n", __func__); + if (wfound) { + /* + * pollfd has the master sockfd in it... we + * need to use that in HANDSHAKE2 to understand + * which wsi to actually write on + */ + lws_client_socket_service(wfound, pollfd, wsi); + lws_callback_on_writable(wsi); + } else + lwsl_debug("%s: didn't find anything in txn q in HS2\n", + __func__); + + lws_vhost_unlock(wsi->vhost); return 0; } diff --git a/lib/roles/http/server/server.c b/lib/roles/http/server/server.c index 2f2224f51..8616b2376 100644 --- a/lib/roles/http/server/server.c +++ b/lib/roles/http/server/server.c @@ -1342,7 +1342,7 @@ deal_body: */ while (1) { - ebuf.len = lws_buflist_next_segment_len( + ebuf.len = (int)lws_buflist_next_segment_len( &wsi->buflist, (uint8_t **)&ebuf.token); if (!ebuf.len) break; @@ -1789,7 +1789,8 @@ lws_http_transaction_completed(struct lws *wsi) } #endif } else { - lwsl_debug("%s: resetting and keeping ah as more pipeline stuff available\n", __func__); + lwsl_debug("%s: resetting and keeping ah as pipeline\n", + __func__); lws_header_table_reset(wsi, 0); /* * If we kept the ah, we should restrict the amount @@ -1804,13 +1805,12 @@ lws_http_transaction_completed(struct lws *wsi) if (wsi->http.ah) wsi->http.ah->ues = URIES_IDLE; - lwsi_set_state(wsi, LRS_ESTABLISHED); + //lwsi_set_state(wsi, LRS_ESTABLISHED); } else if (lws_buflist_next_segment_len(&wsi->buflist, NULL)) if (lws_header_table_attach(wsi, 0)) lwsl_debug("acquired ah\n"); - lwsl_info("%s: %p: keep-alive await new transaction\n", __func__, wsi); lws_callback_on_writable(wsi); @@ -2063,6 +2063,7 @@ 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; @@ -2070,8 +2071,13 @@ adopt_socket_readbuf(struct lws *wsi, const char *readbuf, size_t len) if (!readbuf || len == 0) return wsi; - if (lws_buflist_append_segment(&wsi->buflist, (const uint8_t *)readbuf, len) < 0) + 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. @@ -2086,7 +2092,6 @@ adopt_socket_readbuf(struct lws *wsi, const char *readbuf, size_t len) if (wsi->http.ah || !lws_header_table_attach(wsi, 0)) { lwsl_notice("%s: calling service on readbuf ah\n", __func__); - pt = &wsi->context->pt[(int)wsi->tsi]; /* unlike a normal connect, we have the headers already * (or the first part of them anyway). diff --git a/lib/roles/private.h b/lib/roles/private.h new file mode 100644 index 000000000..f13e14aeb --- /dev/null +++ b/lib/roles/private.h @@ -0,0 +1,273 @@ +/* + * libwebsockets - small server side websockets and web server implementation + * + * Copyright (C) 2010 - 2018 Andy Green + * + * 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 + * + * This is included from private-libwebsockets.h + */ + +typedef uint32_t lws_wsi_state_t; + +/* + * The wsi->role_ops pointer decides almost everything about what role the wsi + * will play, h2, raw, ws, etc. + * + * However there are a few additional flags needed that vary, such as if the + * role is a client or server side, if it has that concept. And the connection + * fulfilling the role, has a separate dynamic state. + * + * 31 16 15 0 + * [ role flags ] [ state ] + * + * The role flags part is generally invariant for the lifetime of the wsi, + * although it can change if the connection role itself does, eg, if the + * connection upgrades from H1 -> WS1 the role flags may be changed at that + * point. + * + * The state part reflects the dynamic connection state, and the states are + * reused between roles. + * + * None of the internal role or state representations are made available outside + * of lws internals. Even for lws internals, if you add stuff here, please keep + * the constants inside this header only by adding necessary helpers here and + * use the helpers in the actual code. This is to ease any future refactors. + * + * Notice LWSIFR_ENCAP means we have a parent wsi that actually carries our + * data as a stream inside a different protocol. + */ + +#define _RS 16 + +#define LWSIFR_CLIENT (0x1000 << _RS) /* client side */ +#define LWSIFR_SERVER (0x2000 << _RS) /* server side */ + +#define LWSIFR_P_ENCAP_H2 (0x0100 << _RS) /* we are encapsulated by h2 */ + +enum lwsi_role { + LWSI_ROLE_MASK = (0xffff << _RS), + LWSI_ROLE_ENCAP_MASK = (0x0f00 << _RS), +}; + +#define lwsi_role(wsi) (wsi->wsistate & LWSI_ROLE_MASK) +#if !defined (_DEBUG) +#define lwsi_set_role(wsi, role) wsi->wsistate = \ + (wsi->wsistate & (~LWSI_ROLE_MASK)) | role +#else +void lwsi_set_role(struct lws *wsi, lws_wsi_state_t role); +#endif + +#define lwsi_role_client(wsi) (!!(wsi->wsistate & LWSIFR_CLIENT)) +#define lwsi_role_server(wsi) (!!(wsi->wsistate & LWSIFR_SERVER)) +#define lwsi_role_h2_ENCAPSULATION(wsi) \ + ((wsi->wsistate & LWSI_ROLE_ENCAP_MASK) == LWSIFR_P_ENCAP_H2) + +/* Pollout wants a callback in this state */ +#define LWSIFS_POCB (0x100) +/* Before any protocol connection was established */ +#define LWSIFS_NOT_EST (0x200) + +enum lwsi_state { + + /* Phase 1: pre-transport */ + + LRS_UNCONNECTED = LWSIFS_NOT_EST | 0, + LRS_WAITING_CONNECT = LWSIFS_NOT_EST | 1, + + /* Phase 2: establishing intermediaries on top of transport */ + + LRS_WAITING_PROXY_REPLY = LWSIFS_NOT_EST | 2, + LRS_WAITING_SSL = LWSIFS_NOT_EST | 3, + LRS_WAITING_SOCKS_GREETING_REPLY = LWSIFS_NOT_EST | 4, + LRS_WAITING_SOCKS_CONNECT_REPLY = LWSIFS_NOT_EST | 5, + LRS_WAITING_SOCKS_AUTH_REPLY = LWSIFS_NOT_EST | 6, + + /* Phase 3: establishing tls tunnel */ + + LRS_SSL_INIT = LWSIFS_NOT_EST | 7, + LRS_SSL_ACK_PENDING = LWSIFS_NOT_EST | 8, + LRS_PRE_WS_SERVING_ACCEPT = LWSIFS_NOT_EST | 9, + + /* Phase 4: connected */ + + LRS_WAITING_SERVER_REPLY = LWSIFS_NOT_EST | 10, + LRS_H2_AWAIT_PREFACE = LWSIFS_NOT_EST | 11, + LRS_H2_AWAIT_SETTINGS = LWSIFS_NOT_EST | + LWSIFS_POCB | 12, + + /* Phase 5: protocol logically established */ + + LRS_H2_CLIENT_SEND_SETTINGS = LWSIFS_POCB | 13, + LRS_H2_WAITING_TO_SEND_HEADERS = LWSIFS_POCB | 14, + LRS_DEFERRING_ACTION = LWSIFS_POCB | 15, + LRS_IDLING = 16, + LRS_H1C_ISSUE_HANDSHAKE = 17, + LRS_H1C_ISSUE_HANDSHAKE2 = 18, + LRS_ISSUE_HTTP_BODY = 19, + LRS_ISSUING_FILE = 20, + LRS_HEADERS = 21, + LRS_BODY = 22, + LRS_ESTABLISHED = LWSIFS_POCB | 23, + + /* Phase 6: finishing */ + + LRS_WAITING_TO_SEND_CLOSE = LWSIFS_POCB | 24, + LRS_RETURNED_CLOSE = LWSIFS_POCB | 25, + LRS_AWAITING_CLOSE_ACK = LWSIFS_POCB | 26, + LRS_FLUSHING_BEFORE_CLOSE = LWSIFS_POCB | 27, + LRS_SHUTDOWN = 28, + + /* Phase 7: dead */ + + LRS_DEAD_SOCKET = 29, + + LRS_MASK = 0xffff +}; + +#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_est(wsi) (!(wsi->wsistate & 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 = \ + (wsi->wsistate & (~LRS_MASK)) | lrs +#else +void lwsi_set_state(struct lws *wsi, lws_wsi_state_t lrs); +#endif + +/* + * internal role-specific ops + */ +struct lws_context_per_thread; +struct lws_role_ops { + const char *name; + const char *alpn; + /* + * After http headers have parsed, this is the last chance for a role + * to upgrade the connection to something else using the headers. + * ws-over-h2 is upgraded from h2 like this. + */ + int (*check_upgrades)(struct lws *wsi); + /* role-specific context init during context creation */ + int (*init_context)(struct lws_context *context, + const struct lws_context_creation_info *info); + /* role-specific per-vhost init during vhost creation */ + int (*init_vhost)(struct lws_vhost *vh, + const struct lws_context_creation_info *info); + /* role-specific per-vhost destructor during vhost destroy */ + int (*destroy_vhost)(struct lws_vhost *vh); + /* generic 1Hz callback for the role itself */ + int (*periodic_checks)(struct lws_context *context, int tsi, + time_t now); + /* chance for the role to force POLLIN without network activity */ + int (*service_flag_pending)(struct lws_context *context, int tsi); + /* an fd using this role has POLLIN signalled */ + int (*handle_POLLIN)(struct lws_context_per_thread *pt, struct lws *wsi, + struct lws_pollfd *pollfd); + /* an fd using the role wanted a POLLOUT callback and now has it */ + int (*handle_POLLOUT)(struct lws *wsi); + /* perform user pollout */ + int (*perform_user_POLLOUT)(struct lws *wsi); + /* do effective callback on writeable */ + int (*callback_on_writable)(struct lws *wsi); + /* connection-specific tx credit in bytes */ + lws_fileofs_t (*tx_credit)(struct lws *wsi); + /* role-specific write formatting */ + int (*write_role_protocol)(struct lws *wsi, unsigned char *buf, + size_t len, enum lws_write_protocol *wp); + + /* get encapsulation parent */ + struct lws * (*encapsulation_parent)(struct lws *wsi); + + /* role-specific destructor */ + int (*alpn_negotiated)(struct lws *wsi, const char *alpn); + + /* chance for the role to handle close in the protocol */ + int (*close_via_role_protocol)(struct lws *wsi, + enum lws_close_status reason); + /* role-specific close processing */ + int (*close_role)(struct lws_context_per_thread *pt, struct lws *wsi); + /* role-specific connection close processing */ + int (*close_kill_connection)(struct lws *wsi, + enum lws_close_status reason); + /* role-specific destructor */ + int (*destroy_role)(struct lws *wsi); + + /* + * the callback reasons for WRITEABLE for client, server + * (just client applies if no concept of client or server) + */ + uint16_t writeable_cb[2]; + /* + * the callback reasons for CLOSE for client, server + * (just client applies if no concept of client or server) + */ + uint16_t close_cb[2]; + + unsigned int file_handle:1; /* role operates on files not sockets */ +}; + +/* core roles */ +extern struct lws_role_ops role_ops_raw_skt, role_ops_raw_file, role_ops_listen, + role_ops_pipe; + +/* bring in role private declarations */ + +#if defined(LWS_ROLE_H1) || defined(LWS_ROLE_H2) + #include "roles/http/private.h" +#else + #define lwsi_role_http(wsi) (0) +#endif + +#if defined(LWS_ROLE_H1) + #include "roles/h1/private.h" +#else + #define lwsi_role_h1(wsi) (0) +#endif + +#if defined(LWS_ROLE_H2) + #include "roles/h2/private.h" +#else + #define lwsi_role_h2(wsi) (0) +#endif + +#if defined(LWS_ROLE_WS) + #include "roles/ws/private.h" +#else + #define lwsi_role_ws(wsi) (0) +#endif + +#if defined(LWS_ROLE_CGI) + #include "roles/cgi/private.h" +#else + #define lwsi_role_cgi(wsi) (0) +#endif + +enum { + LWS_HP_RET_BAIL_OK, + LWS_HP_RET_BAIL_DIE, + LWS_HP_RET_USER_SERVICE, + + LWS_HPI_RET_WSI_ALREADY_DIED, /* we closed it */ + LWS_HPI_RET_HANDLED, /* no probs */ + LWS_HPI_RET_PLEASE_CLOSE_ME, /* close it for us */ + + LWS_UPG_RET_DONE, + LWS_UPG_RET_CONTINUE, + LWS_UPG_RET_BAIL +}; diff --git a/lib/roles/ws/server-ws.c b/lib/roles/ws/server-ws.c index 832791862..0d8a0b98c 100644 --- a/lib/roles/ws/server-ws.c +++ b/lib/roles/ws/server-ws.c @@ -568,7 +568,7 @@ lws_ws_frame_rest_is_payload(struct lws *wsi, uint8_t **buf, size_t len) { uint8_t *buffer = *buf, mask[4]; struct lws_tokens ebuf; - unsigned int avail = len; + unsigned int avail = (unsigned int)len; #if !defined(LWS_WITHOUT_EXTENSIONS) unsigned int old_packet_length = (int)wsi->ws->rx_packet_length; #endif diff --git a/lib/service.c b/lib/service.c index 291995cf2..0dd4b3589 100644 --- a/lib/service.c +++ b/lib/service.c @@ -351,33 +351,64 @@ lws_service_adjust_timeout(struct lws_context *context, int timeout_ms, int tsi) return timeout_ms; } - +/* + * POLLIN said there is something... we must read it, and either use it; or + * if other material already in the buflist append it and return the buflist + * head material. + */ int lws_buflist_aware_read(struct lws_context_per_thread *pt, struct lws *wsi, struct lws_tokens *ebuf) { - ebuf->len = (int)lws_buflist_next_segment_len(&wsi->buflist, - (uint8_t **)&ebuf->token); - if (!ebuf->len) { - ebuf->token = (char *)pt->serv_buf; - ebuf->len = lws_ssl_capable_read(wsi, pt->serv_buf, - wsi->context->pt_serv_buf_size); + int n, prior = (int)lws_buflist_next_segment_len(&wsi->buflist, NULL); - // if (ebuf->len > 0) - // lwsl_hexdump_notice(ebuf->token, ebuf->len); + ebuf->token = (char *)pt->serv_buf; + ebuf->len = lws_ssl_capable_read(wsi, pt->serv_buf, + wsi->context->pt_serv_buf_size); - return 0; /* fresh */ + if (ebuf->len == LWS_SSL_CAPABLE_MORE_SERVICE && prior) + goto get_from_buflist; + + if (ebuf->len <= 0) + return 0; + + /* nothing in buflist already? Then just use what we read */ + + if (!prior) + return 0; + + /* stash what we read */ + + n = lws_buflist_append_segment(&wsi->buflist, (uint8_t *)ebuf->token, + ebuf->len); + if (n < 0) + return -1; + if (n) { + lwsl_debug("%s: added %p to rxflow list\n", __func__, wsi); + lws_dll_lws_add_front(&wsi->dll_buflist, &pt->dll_head_buflist); } - return 1; /* buffered */ + /* get the first buflist guy in line */ + +get_from_buflist: + + ebuf->len = (int)lws_buflist_next_segment_len(&wsi->buflist, + (uint8_t **)&ebuf->token); + + return 1; /* came from buflist */ } int lws_buflist_aware_consume(struct lws *wsi, struct lws_tokens *ebuf, int used, int buffered) { + struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi]; int m; + /* it's in the buflist; we didn't use any */ + + if (!used && buffered) + return 0; if (used && buffered) { m = lws_buflist_use_segment(&wsi->buflist, used); @@ -394,10 +425,17 @@ lws_buflist_aware_consume(struct lws *wsi, struct lws_tokens *ebuf, int used, /* any remainder goes on the buflist */ - if (used != ebuf->len && - lws_buflist_append_segment(&wsi->buflist, (uint8_t *)ebuf->token + - used, ebuf->len - used) < 0) - return 1; /* OOM */ + if (used != ebuf->len) { + m = lws_buflist_append_segment(&wsi->buflist, + (uint8_t *)ebuf->token + used, + ebuf->len - used); + if (m < 0) + return 1; /* OOM */ + if (m) { + lwsl_debug("%s: added %p to rxflow list\n", __func__, wsi); + lws_dll_lws_add_front(&wsi->dll_buflist, &pt->dll_head_buflist); + } + } return 0; } diff --git a/lwsws/main.c b/lwsws/main.c index 1a89f852a..fba948ef6 100644 --- a/lwsws/main.c +++ b/lwsws/main.c @@ -316,9 +316,11 @@ int main(int argc, char **argv) } lws_context_destroy(context); - + (void)budget; +#if (UV_VERSION_MAJOR > 0) // Travis... while ((n = uv_loop_close(&loop)) && --budget) uv_run(&loop, UV_RUN_ONCE); +#endif fprintf(stderr, "lwsws exited cleanly: %d\n", n); diff --git a/minimal-examples/http-client/minimal-http-client-hugeurl/selftest.sh b/minimal-examples/http-client/minimal-http-client-hugeurl/selftest.sh index af8a4bcb1..2da54b669 100755 --- a/minimal-examples/http-client/minimal-http-client-hugeurl/selftest.sh +++ b/minimal-examples/http-client/minimal-http-client-hugeurl/selftest.sh @@ -33,9 +33,9 @@ wait $SPID 2>/dev/null if [ -z "$TRAVIS_OS_NAME" ] ; then SPID="" - spawn "" $5/http-server/minimal-http-server-libuv $1/lws-minimal-http-server-libuv -s + spawn "" $5/http-server/minimal-http-server-eventlib $1/lws-minimal-http-server-eventlib --uv -s dotest $1 $2 localhost-suv -l - spawn $SPID $5/http-server/minimal-http-server-libuv $1/lws-minimal-http-server-libuv -s + spawn $SPID $5/http-server/minimal-http-server-eventlib $1/lws-minimal-http-server-eventlib --uv -s dotest $1 $2 localhost-suv-h1 -l --h1 kill $SPID 2>/dev/null diff --git a/minimal-examples/http-client/minimal-http-client-multi/minimal-http-client-multi.c b/minimal-examples/http-client/minimal-http-client-multi/minimal-http-client-multi.c index e7c8d076b..19940ffeb 100644 --- a/minimal-examples/http-client/minimal-http-client-multi/minimal-http-client-multi.c +++ b/minimal-examples/http-client/minimal-http-client-multi/minimal-http-client-multi.c @@ -94,7 +94,8 @@ callback_http(struct lws *wsi, enum lws_callback_reasons reason, return 0; /* don't passthru */ case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: - lwsl_user("LWS_CALLBACK_COMPLETED_CLIENT_HTTP %d\n", u->index); + lwsl_user("LWS_CALLBACK_COMPLETED_CLIENT_HTTP %p: idx %d\n", + wsi, u->index); client_wsi[u->index] = NULL; if (++completed == COUNT) { if (!failed) @@ -166,7 +167,7 @@ lws_try_client_connection(struct lws_client_connect_info *i, int m) interrupted = 1; } } else - lwsl_user("started connection %d\n", m); + lwsl_user("started connection %p: idx %d\n", client_wsi[m], m); } int main(int argc, const char **argv) @@ -201,7 +202,6 @@ int main(int argc, const char **argv) info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */ info.protocols = protocols; - info.max_http_header_pool = 20; #if defined(LWS_WITH_MBEDTLS) /* @@ -237,6 +237,9 @@ int main(int argc, const char **argv) i.address = "warmcat.com"; } + if ((p = lws_cmdline_option(argc, argv, "--port"))) + i.port = atoi(p); + i.host = i.address; i.origin = i.address; i.method = "GET"; @@ -267,7 +270,7 @@ int main(int argc, const char **argv) if (m == (int)LWS_ARRAY_SIZE(client_wsi) - 1) next = us() + 1000000; else - next = us() + 100000; + next = us() + 300000; } } diff --git a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/CMakeLists.txt b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/CMakeLists.txt index 6ad94936b..2d804a70c 100644 --- a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/CMakeLists.txt +++ b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/CMakeLists.txt @@ -1,8 +1,8 @@ cmake_minimum_required(VERSION 2.8) include(CheckCSourceCompiles) -set(SAMP lws-minimal-http-server-libuv-foreign) -set(SRCS minimal-http-server-libuv-foreign.c) +set(SAMP lws-minimal-http-server-eventlib-foreign) +set(SRCS minimal-http-server-eventlib-foreign.c) # If we are being built as part of lws, confirm current build config supports # reqconfig, else skip building ourselves. @@ -63,16 +63,25 @@ ENDMACRO() set(requirements 1) require_lws_config(LWS_ROLE_H1 1 requirements) -require_lws_config(LWS_WITH_LIBUV 1 requirements) require_lws_config(LWS_WITHOUT_SERVER 0 requirements) -CHECK_C_SOURCE_COMPILES("#include \n#include \nint main(void) {\n#if (UV_VERSION_MAJOR > 0)\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_RECENT_LIBUV) -if (NOT HAS_RECENT_LIBUV) - if (LWS_WITH_MINIMAL_EXAMPLES) - message("libuv is too old (pre- 1.0)") - else() - message(FATAL_ERROR "libuv is too old (pre- 1.0)") - endif() +CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(LWS_WITH_LIBUV)\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" LWS_WITH_LIBUV) +CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(LWS_WITH_LIBEVENT)\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" LWS_WITH_LIBEVENT) +CHECK_C_SOURCE_COMPILES("#include \nint main(void) {\n#if defined(LWS_WITH_LIBEV)\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" LWS_WITH_LIBEV) + +if (LWS_WITH_LIBUV) + set(extralibs ${extralibs} uv) +endif() +if (LWS_WITH_LIBEVENT) + set(extralibs ${extralibs} event) +endif() +if (LWS_WITH_LIBEV) + set(extralibs ${extralibs} ev) +endif() + +message("Extra libs: ${extralibs}") + +if (NOT LWS_WITH_LIBUV AND NOT LWS_WITH_LIBEVENT AND NOT LWS_WITH_LIBEV) set(requirements 0) endif() @@ -80,9 +89,9 @@ if (requirements) add_executable(${SAMP} ${SRCS}) if (websockets_shared) - target_link_libraries(${SAMP} websockets_shared uv) + target_link_libraries(${SAMP} websockets_shared ${extralibs}) add_dependencies(${SAMP} websockets_shared) else() - target_link_libraries(${SAMP} websockets uv) + target_link_libraries(${SAMP} websockets ${extralibs}) endif() endif() diff --git a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/README.md b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/README.md index 41cc9d93a..a663d6e81 100644 --- a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/README.md +++ b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/README.md @@ -1,4 +1,19 @@ -# lws minimal http server libuv foreign +# lws minimal http server eventlib foreign + +Commandline option|Meaning +---|--- +-d |Debug verbosity in decimal, eg, -d15 +--uv|Use the libuv event library (lws must have been configured with `-DLWS_WITH_LIBUV=1`) +--event|Use the libevent library (lws must have been configured with `-DLWS_WITH_LIBEVENT=1`) +--ev|Use the libev event library (lws must have been configured with `-DLWS_WITH_LIBEV=1`) + +Notice libevent and libev cannot coexist in the one library. But all the other combinations are OK. + +x|libuv|libevent|libev +---|---|---|--- +libuv|-|OK|OK +libevent|OK|-|no +libev|OK|no|- This demonstrates having lws take part in a libuv loop owned by something else, with its own objects running in the loop. diff --git a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/minimal-http-server-eventlib-foreign.c b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/minimal-http-server-eventlib-foreign.c new file mode 100644 index 000000000..3e807d9c8 --- /dev/null +++ b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/minimal-http-server-eventlib-foreign.c @@ -0,0 +1,424 @@ +/* + * lws-minimal-http-server-eventlib-foreign + * + * Copyright (C) 2018 Andy Green + * + * This file is made available under the Creative Commons CC0 1.0 + * Universal Public Domain Dedication. + * + * This demonstrates the most minimal http server you can make with lws that + * uses a libuv event loop created outside lws. It shows how lws can + * participate in someone else's event loop and clean up after itself. + * + * You choose the event loop to work with at runtime, by giving the + * --uv, --event or --ev switch. Lws has to have been configured to build the + * selected event lib support. + * + * To keep it simple, it serves stuff from the subdirectory + * "./mount-origin" of the directory it was started in. + * You can change that by changing mount.origin below. + */ + +#include +#include +#include + +struct lws_context_creation_info info; +static struct lws_context *context; +static int lifetime = 5, reported; + +static void foreign_timer_service(void *foreign_loop); + +enum { + TEST_STATE_CREATE_LWS_CONTEXT, + TEST_STATE_DESTROY_LWS_CONTEXT, + TEST_STATE_EXIT +}; + +static int sequence = TEST_STATE_CREATE_LWS_CONTEXT; + +static const struct lws_http_mount mount = { + /* .mount_next */ NULL, /* linked-list "next" */ + /* .mountpoint */ "/", /* mountpoint URL */ + /* .origin */ "./mount-origin", /* serve from dir */ + /* .def */ "index.html", /* default filename */ + /* .protocol */ NULL, + /* .cgienv */ NULL, + /* .extra_mimetypes */ NULL, + /* .interpret */ NULL, + /* .cgi_timeout */ 0, + /* .cache_max_age */ 0, + /* .auth_mask */ 0, + /* .cache_reusable */ 0, + /* .cache_revalidate */ 0, + /* .cache_intermediaries */ 0, + /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ + /* .mountpoint_len */ 1, /* char count */ + /* .basic_auth_login_file */ NULL, +}; + +static void +signal_cb(int signum) +{ + lwsl_notice("Signal %d caught, exiting...\n", signum); + + switch (signum) { + case SIGTERM: + case SIGINT: + break; + default: + break; + } + + lws_context_destroy(context); +} + +/* + * The event-loop specific foreign loop code, one set for each event loop lib + * + * Only the code in this section is specific to the event library used. + */ + +#if defined(LWS_WITH_LIBUV) + +static uv_loop_t loop_uv; +static uv_timer_t timer_outer_uv; +static uv_signal_t sighandler_uv; + +static void +timer_cb_uv(uv_timer_t *t) +{ + foreign_timer_service(&loop_uv); +} + +static void +signal_cb_uv(uv_signal_t *watcher, int signum) +{ + signal_cb(signum); +} + +static void +foreign_event_loop_init_and_run_libuv(void) +{ + /* we create and start our "foreign loop" */ + +#if (UV_VERSION_MAJOR > 0) // Travis... + uv_loop_init(&loop_uv); +#endif + uv_signal_init(&loop_uv, &sighandler_uv); + uv_signal_start(&sighandler_uv, signal_cb_uv, SIGINT); + + uv_timer_init(&loop_uv, &timer_outer_uv); +#if (UV_VERSION_MAJOR > 0) // Travis... + uv_timer_start(&timer_outer_uv, timer_cb_uv, 0, 1000); +#else + (void)timer_cb_uv; +#endif + + uv_run(&loop_uv, UV_RUN_DEFAULT); +} + +static void +foreign_event_loop_stop_libuv(void) +{ + uv_stop(&loop_uv); +} + +static void +foreign_event_loop_cleanup_libuv(void) +{ + /* cleanup the foreign loop assets */ + + uv_timer_stop(&timer_outer_uv); + uv_close((uv_handle_t*)&timer_outer_uv, NULL); + uv_signal_stop(&sighandler_uv); + uv_close((uv_handle_t *)&sighandler_uv, NULL); + + uv_run(&loop_uv, UV_RUN_DEFAULT); +#if (UV_VERSION_MAJOR > 0) // Travis... + uv_loop_close(&loop_uv); +#endif +} + +#endif + +#if defined(LWS_WITH_LIBEVENT) + +static struct event_base *loop_event; +static struct event *timer_outer_event; +static struct event *sighandler_event; + +static void +timer_cb_event(int fd, short event, void *arg) +{ + foreign_timer_service(loop_event); +} + +static void +signal_cb_event(int fd, short event, void *arg) +{ + signal_cb((int)(lws_intptr_t)arg); +} + +static void +foreign_event_loop_init_and_run_libevent(void) +{ + struct timeval tv; + + /* we create and start our "foreign loop" */ + + tv.tv_sec = 1; + tv.tv_usec = 0; + + loop_event = event_base_new(); + + sighandler_event = evsignal_new(loop_event, SIGINT, signal_cb_event, + (void*)SIGINT); + + timer_outer_event = event_new(loop_event, -1, EV_PERSIST, + timer_cb_event, NULL); + //evtimer_new(loop_event, timer_cb_event, NULL); + evtimer_add(timer_outer_event, &tv); + + event_base_loop(loop_event, 0); +} + +static void +foreign_event_loop_stop_libevent(void) +{ + event_base_loopexit(loop_event, NULL); +} + +static void +foreign_event_loop_cleanup_libevent(void) +{ + /* cleanup the foreign loop assets */ + + evtimer_del(timer_outer_event); + event_free(timer_outer_event); + evsignal_del(sighandler_event); + event_free(sighandler_event); + + event_base_loop(loop_event, 0); + event_base_free(loop_event); +} + +#endif + +#if defined(LWS_WITH_LIBEV) + +static struct ev_loop *loop_ev; +static struct ev_timer timer_outer_ev; +static struct ev_signal sighandler_ev; + +static void +timer_cb_ev(struct ev_loop *loop, struct ev_timer *watcher, int revents) +{ + foreign_timer_service(loop_ev); +} + +static void +signal_cb_ev(struct ev_loop *loop, struct ev_signal *watcher, int revents) +{ + signal_cb(watcher->signum); +} + +static void +foreign_event_loop_init_and_run_libev(void) +{ + /* we create and start our "foreign loop" */ + + loop_ev = ev_loop_new(0); + + ev_signal_init(&sighandler_ev, signal_cb_ev, SIGINT); + ev_signal_start(loop_ev, &sighandler_ev); + + ev_timer_init(&timer_outer_ev, timer_cb_ev, 0, 1); + ev_timer_start(loop_ev, &timer_outer_ev); + + ev_run(loop_ev, 0); +} + +static void +foreign_event_loop_stop_libev(void) +{ + ev_break(loop_ev, EVBREAK_ALL); +} + +static void +foreign_event_loop_cleanup_libev(void) +{ + /* cleanup the foreign loop assets */ + + ev_timer_stop(loop_ev, &timer_outer_ev); + ev_signal_stop(loop_ev, &sighandler_ev); + + ev_run(loop_ev, UV_RUN_DEFAULT); + ev_loop_destroy(loop_ev); +} + +#endif + +/* this is called at 1Hz using a foreign loop timer */ + +static void +foreign_timer_service(void *foreign_loop) +{ + void *foreign_loops[1]; + + lwsl_user("Foreign 1Hz timer\n"); + + if (sequence == TEST_STATE_EXIT && !context && !reported) { + /* + * at this point the lws_context_destroy() we did earlier + * has completed and the entire context is wholly destroyed + */ + lwsl_user("lws_destroy_context() done, continuing for 5s\n"); + reported = 1; + } + + if (--lifetime) + return; + + switch (sequence++) { + case TEST_STATE_CREATE_LWS_CONTEXT: + /* this only has to exist for the duration of create context */ + foreign_loops[0] = foreign_loop; + info.foreign_loops = foreign_loops; + + context = lws_create_context(&info); + if (!context) { + lwsl_err("lws init failed\n"); + return; + } + lwsl_user("LWS Context created and will be active for 10s\n"); + lifetime = 11; + break; + + case TEST_STATE_DESTROY_LWS_CONTEXT: + /* cleanup the lws part */ + lwsl_user("Destroying lws context and continuing loop for 5s\n"); + lws_context_destroy(context); + lifetime = 6; + break; + + case TEST_STATE_EXIT: + lwsl_user("Deciding to exit foreign loop too\n"); +#if defined(LWS_WITH_LIBUV) + if (info.options & LWS_SERVER_OPTION_LIBUV) + foreign_event_loop_stop_libuv(); +#endif +#if defined(LWS_WITH_LIBEVENT) + if (info.options & LWS_SERVER_OPTION_LIBEVENT) + foreign_event_loop_stop_libevent(); +#endif +#if defined(LWS_WITH_LIBEV) + if (info.options & LWS_SERVER_OPTION_LIBEV) + foreign_event_loop_stop_libev(); +#endif + break; + default: + break; + } +} + +int main(int argc, const char **argv) +{ + const char *p; + int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE + /* for LLL_ verbosity above NOTICE to be built into lws, + * lws must have been configured and built with + * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ + /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ + /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ + /* | LLL_DEBUG */; + + if ((p = lws_cmdline_option(argc, argv, "-d"))) + logs = atoi(p); + + lws_set_log_level(logs, NULL); + lwsl_user("LWS minimal http server libuv + foreign loop |" + " visit http://localhost:7681\n"); + + /* + * We prepare the info here, but don't use it until later in the + * timer callback, to demonstrate the independence of the foreign loop + * and lws. + */ + + memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ + info.port = 7681; + info.mounts = &mount; + info.error_document_404 = "/404.html"; + info.pcontext = &context; + + if (lws_cmdline_option(argc, argv, "-s")) { + info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; + info.ssl_cert_filepath = "localhost-100y.cert"; + info.ssl_private_key_filepath = "localhost-100y.key"; + } + + if (lws_cmdline_option(argc, argv, "--uv")) + info.options |= LWS_SERVER_OPTION_LIBUV; + else + if (lws_cmdline_option(argc, argv, "--event")) + info.options |= LWS_SERVER_OPTION_LIBEVENT; + else + if (lws_cmdline_option(argc, argv, "--ev")) + info.options |= LWS_SERVER_OPTION_LIBEV; + else { + lwsl_err("This app only makes sense when used\n"); + lwsl_err(" with a foreign loop, --uv, --event, or --ev\n"); + + return 1; + } + + lwsl_user(" This app creates a foreign event loop with a timer +\n"); + lwsl_user(" signalhandler, and performs a test in three phases:\n"); + lwsl_user("\n"); + lwsl_user(" 1) 5s: Runs the loop with just the timer\n"); + lwsl_user(" 2) 10s: create an lws context serving on localhost:7681\n"); + lwsl_user(" using the same foreign loop. Destroy it after 10s.\n"); + lwsl_user(" 3) 5s: Run the loop again with just the timer\n"); + lwsl_user("\n"); + lwsl_user(" Finally close only the timer and signalhandler and\n"); + lwsl_user(" exit the loop cleanly\n"); + + lwsl_notice("%s\n", info.ssl_cert_filepath); + + /* foreign loop specific startup and run */ + +#if defined(LWS_WITH_LIBUV) + if (info.options & LWS_SERVER_OPTION_LIBUV) + foreign_event_loop_init_and_run_libuv(); +#endif +#if defined(LWS_WITH_LIBEVENT) + if (info.options & LWS_SERVER_OPTION_LIBEVENT) + foreign_event_loop_init_and_run_libevent(); +#endif +#if defined(LWS_WITH_LIBEV) + if (info.options & LWS_SERVER_OPTION_LIBEV) + foreign_event_loop_init_and_run_libev(); +#endif + + lws_context_destroy(context); + + /* foreign loop specific cleanup and exit */ + +#if defined(LWS_WITH_LIBUV) + if (info.options & LWS_SERVER_OPTION_LIBUV) + foreign_event_loop_cleanup_libuv(); +#endif +#if defined(LWS_WITH_LIBEVENT) + if (info.options & LWS_SERVER_OPTION_LIBEVENT) + foreign_event_loop_cleanup_libevent(); +#endif +#if defined(LWS_WITH_LIBEV) + if (info.options & LWS_SERVER_OPTION_LIBEV) + foreign_event_loop_cleanup_libev(); +#endif + + lwsl_user("%s: exiting...\n", __func__); + + return 0; +} diff --git a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/minimal-http-server-libuv-foreign.c b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/minimal-http-server-libuv-foreign.c deleted file mode 100644 index 7108133ee..000000000 --- a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/minimal-http-server-libuv-foreign.c +++ /dev/null @@ -1,201 +0,0 @@ -/* - * lws-minimal-http-server-libuv-foreign - * - * Copyright (C) 2018 Andy Green - * - * This file is made available under the Creative Commons CC0 1.0 - * Universal Public Domain Dedication. - * - * This demonstrates the most minimal http server you can make with lws that - * uses a libuv event loop created outside lws. It shows how lws can - * participate in someone else's event loop and clean up after itself. - * - * To keep it simple, it serves stuff from the subdirectory - * "./mount-origin" of the directory it was started in. - * You can change that by changing mount.origin below. - */ - -#include -#include -#include - -static struct lws_context *context; -static uv_loop_t loop; -static int lifetime = 5, reported; -struct lws_context_creation_info info; - -enum { - TEST_STATE_CREATE_LWS_CONTEXT, - TEST_STATE_DESTROY_LWS_CONTEXT, - TEST_STATE_EXIT -}; - -static int sequence = TEST_STATE_CREATE_LWS_CONTEXT; - -static const struct lws_http_mount mount = { - /* .mount_next */ NULL, /* linked-list "next" */ - /* .mountpoint */ "/", /* mountpoint URL */ - /* .origin */ "./mount-origin", /* serve from dir */ - /* .def */ "index.html", /* default filename */ - /* .protocol */ NULL, - /* .cgienv */ NULL, - /* .extra_mimetypes */ NULL, - /* .interpret */ NULL, - /* .cgi_timeout */ 0, - /* .cache_max_age */ 0, - /* .auth_mask */ 0, - /* .cache_reusable */ 0, - /* .cache_revalidate */ 0, - /* .cache_intermediaries */ 0, - /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ - /* .mountpoint_len */ 1, /* char count */ - /* .basic_auth_login_file */ NULL, -}; - -void signal_cb(uv_signal_t *watcher, int signum) -{ - lwsl_notice("Signal %d caught, exiting...\n", watcher->signum); - - switch (watcher->signum) { - case SIGTERM: - case SIGINT: - break; - default: - signal(SIGABRT, SIG_DFL); - abort(); - break; - } - - if (context) - lws_context_destroy(context); -} - -/* this logs once a second to show that the foreign loop assets are working */ - -static void -timer_cb(uv_timer_t *t) -{ - void *foreign_loops[1]; - - foreign_loops[0] = &loop; - info.foreign_loops = foreign_loops; - - lwsl_user("Foreign 1Hz timer\n"); - - if (sequence == TEST_STATE_EXIT && !context && !reported) { - /* - * at this point the lws_context_destroy() we did earlier - * has completed and the entire context is wholly destroyed - */ - lwsl_user("lws_destroy_context() completed, continuing for 5s\n"); - reported = 1; - } - - if (--lifetime) - return; - - switch (sequence++) { - case TEST_STATE_CREATE_LWS_CONTEXT: - context = lws_create_context(&info); - if (!context) { - lwsl_err("lws init failed\n"); - return; - } - lwsl_user("LWS Context created and active for 10s\n"); - lifetime = 11; - break; - case TEST_STATE_DESTROY_LWS_CONTEXT: - /* cleanup the lws part */ - lwsl_user("Destroying lws context and continuing loop for 5s\n"); - lws_context_destroy(context); - lifetime = 6; - break; - - case TEST_STATE_EXIT: - lwsl_user("Deciding to exit foreign loop too\n"); - uv_stop(&loop); - break; - default: - break; - } -} - - -int main(int argc, const char **argv) -{ - uv_timer_t timer_outer; - uv_signal_t sighandler; - const char *p; - int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE - /* for LLL_ verbosity above NOTICE to be built into lws, - * lws must have been configured and built with - * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */ - /* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */ - /* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */ - /* | LLL_DEBUG */; - - if ((p = lws_cmdline_option(argc, argv, "-d"))) - logs = atoi(p); - - lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http server libuv + foreign loop |" - " visit http://localhost:7681\n"); - - /* - * We prepare the info here, but don't use it until later in the - * timer callback, to demonstrate the independence of the foreign loop - * and lws. - */ - - memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ - info.port = 7681; - info.mounts = &mount; - info.error_document_404 = "/404.html"; - info.options = LWS_SERVER_OPTION_LIBUV; - info.pcontext = &context; - if (lws_cmdline_option(argc, argv, "-s")) { - info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; - info.ssl_cert_filepath = "localhost-100y.cert"; - info.ssl_private_key_filepath = "localhost-100y.key"; - } - info.pcontext = &context; - - lwsl_user(" This app creates a uv loop with a timer + signalhandler, and\n"); - lwsl_user(" performs a test in three phases:\n"); - lwsl_user("\n"); - lwsl_user(" 1) 5s: Runs the loop with just the timer\n"); - lwsl_user(" 2) 10s: create an lws context serving on localhost:7681\n"); - lwsl_user(" using the same uv loop. Destroy it after 10s.\n"); - lwsl_user(" 3) 5s: Run the loop again with just the timer\n"); - lwsl_user("\n"); - lwsl_user(" Finally close only the timer and signalhandler and\n"); - lwsl_user(" exit the loop cleanly\n"); - - /* we create and start our "foreign loop" */ - - uv_loop_init(&loop); - uv_signal_init(&loop, &sighandler); - uv_signal_start(&sighandler, signal_cb, SIGINT); - - uv_timer_init(&loop, &timer_outer); - uv_timer_start(&timer_outer, timer_cb, 0, 1000); - - uv_run(&loop, UV_RUN_DEFAULT); - - /* in the case we hit ^C while lws still exists */ - lws_context_destroy(context); - - /* cleanup the foreign loop assets */ - - uv_timer_stop(&timer_outer); - uv_close((uv_handle_t*)&timer_outer, NULL); - uv_signal_stop(&sighandler); - uv_close((uv_handle_t *)&sighandler, NULL); - - uv_run(&loop, UV_RUN_DEFAULT); - uv_loop_close(&loop); - - lwsl_user("%s: exiting...\n", __func__); - - return 0; -} diff --git a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/mount-origin/index.html b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/mount-origin/index.html index 31a6dfaf2..3ec21dffb 100644 --- a/minimal-examples/http-server/minimal-http-server-eventlib-foreign/mount-origin/index.html +++ b/minimal-examples/http-server/minimal-http-server-eventlib-foreign/mount-origin/index.html @@ -3,10 +3,10 @@
- Hello from the minimal http server libuv foreign loop example. + Hello from the minimal http server eventlib foreign loop example.
The timer messages in the console are coming from
- a timer on the libuv loop set up before the lws context
+ a timer on the event library lib loop set up before the lws context
started using it. diff --git a/minimal-examples/http-server/minimal-http-server-eventlib/minimal-http-server-libuv.c b/minimal-examples/http-server/minimal-http-server-eventlib/minimal-http-server-eventlib.c similarity index 72% rename from minimal-examples/http-server/minimal-http-server-eventlib/minimal-http-server-libuv.c rename to minimal-examples/http-server/minimal-http-server-eventlib/minimal-http-server-eventlib.c index 9d73be132..4b9d3c996 100644 --- a/minimal-examples/http-server/minimal-http-server-eventlib/minimal-http-server-libuv.c +++ b/minimal-examples/http-server/minimal-http-server-eventlib/minimal-http-server-eventlib.c @@ -1,13 +1,13 @@ /* - * lws-minimal-http-server-libuv + * lws-minimal-http-server-eventlib * * Copyright (C) 2018 Andy Green * * This file is made available under the Creative Commons CC0 1.0 * Universal Public Domain Dedication. * - * This demonstrates the most minimal http server you can make with lws using - * the libuv event loop. + * This demonstrates a minimal http[s] server that can work with any of the + * supported event loop backends, or the default poll() one. * * To keep it simple, it serves stuff from the subdirectory * "./mount-origin" of the directory it was started in. @@ -42,22 +42,22 @@ static const struct lws_http_mount mount = { void signal_cb(void *handle, int signum) { - uv_signal_t *watcher = (uv_signal_t *)handle; - - lwsl_notice("Signal %d caught, exiting...\n", watcher->signum); - - switch (watcher->signum) { + switch (signum) { case SIGTERM: case SIGINT: break; default: - signal(SIGABRT, SIG_DFL); - abort(); + lwsl_err("%s: signal %d\n", __func__, signum); break; } lws_context_destroy(context); } +void sigint_handler(int sig) +{ + signal_cb(NULL, sig); +} + int main(int argc, const char **argv) { struct lws_context_creation_info info; @@ -74,19 +74,32 @@ int main(int argc, const char **argv) logs = atoi(p); lws_set_log_level(logs, NULL); - lwsl_user("LWS minimal http server libuv [-s (ssl)] | visit http://localhost:7681\n"); + lwsl_user("LWS minimal http server eventlib | visit http://localhost:7681\n"); + lwsl_user(" [-s (ssl)] [--uv (libuv)] [--ev (libev)] [--event (libevent)]\n"); memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ info.port = 7681; info.mounts = &mount; info.error_document_404 = "/404.html"; + info.pcontext = &context; + info.signal_cb = signal_cb; + if (lws_cmdline_option(argc, argv, "-s")) { info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; info.ssl_cert_filepath = "localhost-100y.cert"; info.ssl_private_key_filepath = "localhost-100y.key"; } - info.options |= LWS_SERVER_OPTION_LIBUV; - info.signal_cb = signal_cb; + + if (lws_cmdline_option(argc, argv, "--uv")) + info.options |= LWS_SERVER_OPTION_LIBUV; + else + if (lws_cmdline_option(argc, argv, "--event")) + info.options |= LWS_SERVER_OPTION_LIBEVENT; + else + if (lws_cmdline_option(argc, argv, "--ev")) + info.options |= LWS_SERVER_OPTION_LIBEV; + else + signal(SIGINT, sigint_handler); context = lws_create_context(&info); if (!context) { @@ -94,8 +107,10 @@ int main(int argc, const char **argv) return 1; } - lws_service(context, 0); + while (!lws_service(context, 0)) + ; + lwsl_info("calling external context destroy\n"); lws_context_destroy(context); return 0; diff --git a/minimal-examples/selftests-library.sh b/minimal-examples/selftests-library.sh index 13f34ddeb..feef8f89b 100755 --- a/minimal-examples/selftests-library.sh +++ b/minimal-examples/selftests-library.sh @@ -79,6 +79,11 @@ dotest() { if [ -e $2/$MYTEST/$T.result ] ; then R=`cat $2/$MYTEST/$T.result` cat $2/$MYTEST/$T.log | tail -n 3 > $2/$MYTEST/$T.time + if [ $R -ne 0 ] ; then + echo + cat $2/$MYTEST/$T.log + echo + fi fi feedback $MYTEST $R $T diff --git a/plugins/protocol_post_demo.c b/plugins/protocol_post_demo.c index 37cb9745a..5c7678146 100644 --- a/plugins/protocol_post_demo.c +++ b/plugins/protocol_post_demo.c @@ -68,6 +68,8 @@ file_upload_cb(void *data, const char *name, const char *filename, (struct per_session_data__post_demo *)data; #if !defined(LWS_WITH_ESP32) int n; + + (void)n; #endif switch (state) { @@ -92,7 +94,7 @@ file_upload_cb(void *data, const char *name, const char *filename, #if !defined(LWS_WITH_ESP32) n = write((int)(long long)pss->fd, buf, len); - lwsl_notice("%s: write %d says %d\n", __func__, len, n); + lwsl_info("%s: write %d says %d\n", __func__, len, n); #else lwsl_notice("%s: Received chunk size %d\n", __func__, len); #endif diff --git a/test-apps/attack.sh b/scripts/attack.sh similarity index 74% rename from test-apps/attack.sh rename to scripts/attack.sh index 38c6cd5f8..66691fcfa 100755 --- a/test-apps/attack.sh +++ b/scripts/attack.sh @@ -16,6 +16,10 @@ LOG=/tmp/lwslog A=`which libwebsockets-test-server` INSTALLED=`dirname $A` +SHAREDIR=$INSTALLED/../share/libwebsockets-test-server +CORPUS=$SHAREDIR/test.html + + CPID= LEN=0 @@ -28,24 +32,26 @@ function check { #dd if=$LOG bs=1 skip=$LEN 2>/dev/null if [ "$1" = "default" ] ; then - diff /tmp/lwscap $INSTALLED/../share/libwebsockets-test-server/test.html > /dev/null + diff /tmp/lwscap $CORPUS > /dev/null if [ $? -ne 0 ] ; then - echo "FAIL: got something other than test.html back" + echo "FAIL: got something other than $CORPUS back" exit 1 fi fi if [ "$1" = "defaultplusforbidden" ] ; then - cat $INSTALLED/../share/libwebsockets-test-server/test.html > /tmp/plusforb + cat $CORPUS > /tmp/plusforb echo -e -n "HTTP/1.0 403 Forbidden\x0d\x0acontent-type: text/html\x0d\x0acontent-length: 38\x0d\x0a\x0d\x0a

403

" >> /tmp/plusforb diff /tmp/lwscap /tmp/plusforb > /dev/null if [ $? -ne 0 ] ; then - cat $INSTALLED/../share/libwebsockets-test-server/test.html > /tmp/plusforb + cat $CORPUS > /tmp/plusforb echo -e -n "HTTP/1.1 403 Forbidden\x0d\x0acontent-type: text/html\x0d\x0acontent-length: 38\x0d\x0a\x0d\x0a

403

" >> /tmp/plusforb diff /tmp/lwscap /tmp/plusforb > /dev/null if [ $? -ne 0 ] ; then - echo "FAIL: got something other than test.html + forbidden back" + echo "FAIL: got something other than $CORPUS + forbidden back" + tail -n 10 /tmp/lwscap + tail -n 100 $LOG exit 1 fi fi @@ -58,8 +64,16 @@ function check { fi fi + if [ "$1" = "notfound" ] ; then + if [ -z "`grep '

404

' /tmp/lwscap`" ] ; then + echo "FAIL: should have told not found" + exit 1 + fi + fi + + if [ "$1" = "rejected" ] ; then - if [ -z "`grep '

406

' /tmp/lwscap`" ] ; then + if [ -z "`grep '

404

' /tmp/lwscap`" ] ; then echo "FAIL: should have told forbidden (test server has no dirs)" exit 1 fi @@ -67,7 +81,7 @@ function check { if [ "$1" = "media" ] ; then - if [ -z "`grep '

415

' /tmp/lwscap`" ] ; then + if [ -z "`grep '

404

' /tmp/lwscap`" ] ; then echo "FAIL: should have told unknown media type" exit 1 fi @@ -126,6 +140,7 @@ echo echo "---- /cgi-bin/settingsjs?UPDATE_SETTINGS=1&Root_Channels_1_Channel_name_http_post=%3F&Root_Channels_1_Channel_location_http_post=%3F" rm -f /tmp/lwscap echo -n -e "GET /cgi-bin/settingsjs?UPDATE_SETTINGS=1&Root_Channels_1_Channel_name_http_post=%3F&Root_Channels_1_Channel_location_http_post=%3F HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap +cat /tmp/lwscap check 1 "UPDATE_SETTINGS=1" check 2 "Root_Channels_1_Channel_name_http_post=?" check 3 "Root_Channels_1_Channel_location_http_post=?" @@ -147,16 +162,16 @@ check 1 "key1_2=value1" check echo -echo "---- ? processing (%2f%2e%2e%2f%2e./test.html?arg=1)" +echo "---- ? processing (%2f%2e%2e%2f%2e./xxtest.html?arg=1)" rm -f /tmp/lwscap -echo -n -e "GET %2f%2e%2e%2f%2e./test.html?arg=1 HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap +echo -n -e "GET %2f%2e%2e%2f%2e./xxtest.html?arg=1 HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap check 1 "arg=1" check echo -echo "---- ? processing (%2f%2e%2e%2f%2e./test.html?arg=/../.)" +echo "---- ? processing (%2f%2e%2e%2f%2e./xxtest.html?arg=/../.)" rm -f /tmp/lwscap -echo -n -e "GET %2f%2e%2e%2f%2e./test.html?arg=/../. HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap +echo -n -e "GET %2f%2e%2e%2f%2e./xxtest.html?arg=/../. HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap check 1 "arg=/../." check @@ -167,23 +182,23 @@ check echo echo "---- spam more than the name buffer of crap" -dd if=/dev/urandom bs=1 count=80 2>/dev/null | nc -i1s $SERVER $PORT +dd if=/dev/urandom bs=1 count=80 2>/dev/null | nc -i1 $SERVER $PORT check echo echo "---- spam 10MB of crap" -dd if=/dev/urandom bs=1 count=655360 | nc -i1s $SERVER $PORT +dd if=/dev/urandom bs=1 count=655360 | nc -i1 $SERVER $PORT check echo echo "---- malformed URI" echo "GET nonsense................................................................................................................" \ - | nc -i1s $SERVER $PORT + | nc -i1 $SERVER $PORT check echo echo "---- missing URI" -echo -n -e "GET HTTP/1.0\x0d\x0a\x0d\x0a" | nc -i1s $SERVER $PORT >/tmp/lwscap +echo -n -e "GET HTTP/1.0\x0d\x0a\x0d\x0a" | nc -i1 $SERVER $PORT >/tmp/lwscap check echo @@ -210,7 +225,7 @@ echo -n -e "GET blah HTTP/1.0\x0d\x0a........................................... "......................................................................................................................." \ "......................................................................................................................." \ "......................................................................................................................." \ - | nc -i1s $SERVER $PORT + | nc -i1 $SERVER $PORT check echo @@ -232,28 +247,13 @@ echo -n -e "GET ................................................................ "......................................................................................................................." \ "......................................................................................................................." \ "......................................................................................................................." \ - | nc -i1s $SERVER $PORT + | nc -i1 $SERVER $PORT check echo echo "---- good request but http payload coming too (test.html served then forbidden)" echo -n -e "GET /test.html HTTP/1.1\x0d\x0a\x0d\x0aILLEGAL-PAYLOAD........................................" \ - "......................................................................................................................." \ - "......................................................................................................................." \ - "......................................................................................................................." \ - "......................................................................................................................." \ - "......................................................................................................................." \ - "......................................................................................................................." \ - "......................................................................................................................." \ - "......................................................................................................................." \ - "......................................................................................................................." \ - "......................................................................................................................." \ - "......................................................................................................................." \ - "......................................................................................................................." \ - "......................................................................................................................." \ - "......................................................................................................................." \ - "......................................................................................................................." \ - | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap + | cat - /dev/zero | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap check defaultplusforbidden check @@ -261,7 +261,8 @@ echo echo "---- nonexistent file" rm -f /tmp/lwscap echo -n -e "GET /nope HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap -check media +cat /tmp/lwscap +check notfound check echo @@ -275,7 +276,7 @@ echo echo "---- directory attack 1 (/../../../../etc/passwd should be /etc/passswd)" rm -f /tmp/lwscap echo -n -e "GET /../../../../etc/passwd HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap -check rejected +check notfound check echo @@ -317,21 +318,21 @@ echo echo "---- directory attack 7 (/%2e%2e%2f../../../etc/passwd should be /etc/passswd)" rm -f /tmp/lwscap echo -e -n "GET /%2e%2e%2f../../../etc/passwd HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap -check rejected +check notfound check echo echo "---- directory attack 8 (%2f%2e%2e%2f%2e./.%2e/.%2e%2fetc/passwd should be /etc/passswd)" rm -f /tmp/lwscap echo -e -n "GET %2f%2e%2e%2f%2e./.%2e/.%2e%2fetc/passwd HTTP/1.0\x0d\x0a\x0d\x0a" | nc $SERVER $PORT | sed '1,/^\r$/d'> /tmp/lwscap -check rejected +check notfound check echo echo "---- http/1.1 pipelining" rm -f /tmp/lwscap wget -O/tmp/lwsdump http://localhost:7681/test.html http://localhost:7681/test.html http://localhost:7681/test.html http://localhost:7681/test.html http://localhost:7681/test.html http://localhost:7681/test.html http://localhost:7681/test.html http://localhost:7681/test.html 2>&1 | grep "Downloaded: 8 files" > /tmp/lwscap -good=`cat $INSTALLED/../share/libwebsockets-test-server/test.html $INSTALLED/../share/libwebsockets-test-server/test.html $INSTALLED/../share/libwebsockets-test-server/test.html $INSTALLED/../share/libwebsockets-test-server/test.html $INSTALLED/../share/libwebsockets-test-server/test.html $INSTALLED/../share/libwebsockets-test-server/test.html $INSTALLED/../share/libwebsockets-test-server/test.html $INSTALLED/../share/libwebsockets-test-server/test.html | md5sum | cut -d' ' -f1` +good=`cat $CORPUS $CORPUS $CORPUS $CORPUS $CORPUS $CORPUS $CORPUS $CORPUS | md5sum | cut -d' ' -f1` if [ "$good" != "`md5sum /tmp/lwsdump | cut -d' ' -f 1`" ] ; then echo "FAIL: mismatched content good=$good received=`md5sum /tmp/lwsdump`" exit 1 @@ -551,13 +552,11 @@ for i in \ R=`rm -f /tmp/lwscap ; echo -n -e "GET $i HTTP/1.0\r\n\r\n" | nc localhost 7681 2>/dev/null >/tmp/lwscap; head -n1 /tmp/lwscap| cut -d' ' -f2` -#cat /tmp/lwscap | head -n1 #echo ==== $R if [ "$R" != "403" ]; then - U=`cat $LOG | grep lws_http_serve | tail -n 1 | cut -d':' -f6 | cut -d' ' -f2` -# echo $U + U=`cat $LOG | grep Method: | tail -n 1 | cut -d"'" -f4 | sed "s|\\'||g"` echo "- \"$i\" -> $R \"$U\"" >>/tmp/results else echo "- \"$i\" -> $R" >>/tmp/results @@ -565,36 +564,36 @@ fi done cat </tmp/lwsresult1 -- "/..../" -> 406 "/..../" -- "/.../." -> 406 "/.../" -- "/...//" -> 406 "/.../" -- "/.../a" -> 406 "/.../a" -- "/.../w" -> 406 "/.../w" -- "/.../?" -> 406 "/.../" +- "/..../" -> 404 "/..../" +- "/.../." -> 404 "/.../" +- "/...//" -> 404 "/.../" +- "/.../a" -> 404 "/.../a" +- "/.../w" -> 404 "/.../w" +- "/.../?" -> 404 "/.../" - "/.../%" -> 403 - "/../.." -> 200 "/" - "/.././" -> 200 "/" -- "/../.a" -> 415 "/.a" -- "/../.w" -> 415 "/.w" +- "/../.a" -> 404 "/.a" +- "/../.w" -> 404 "/.w" - "/../.." -> 200 "/" - "/../.%" -> 403 - "/..//." -> 200 "/" - "/..///" -> 200 "/" -- "/..//a" -> 415 "/a" -- "/..//w" -> 415 "/w" +- "/..//a" -> 404 "/a" +- "/..//w" -> 404 "/w" - "/..//?" -> 200 "/" - "/..//%" -> 403 -- "/../a." -> 415 "/a." -- "/../a/" -> 406 "/a/" -- "/../aa" -> 415 "/aa" -- "/../aw" -> 415 "/aw" -- "/../a?" -> 415 "/a" +- "/../a." -> 404 "/a." +- "/../a/" -> 404 "/a/" +- "/../aa" -> 404 "/aa" +- "/../aw" -> 404 "/aw" +- "/../a?" -> 404 "/a" - "/../a%" -> 403 -- "/../w." -> 415 "/w." -- "/../w/" -> 406 "/w/" -- "/../wa" -> 415 "/wa" -- "/../ww" -> 415 "/ww" -- "/../w?" -> 415 "/w" +- "/../w." -> 404 "/w." +- "/../w/" -> 404 "/w/" +- "/../wa" -> 404 "/wa" +- "/../ww" -> 404 "/ww" +- "/../w?" -> 404 "/w" - "/../w%" -> 403 - "/../?." -> 200 "/" - "/../?/" -> 200 "/" @@ -608,49 +607,49 @@ cat </tmp/lwsresult1 - "/../%w" -> 403 - "/../%?" -> 403 - "/../%%" -> 403 -- "/./..." -> 415 "/..." +- "/./..." -> 404 "/..." - "/./../" -> 200 "/" -- "/./..a" -> 415 "/..a" -- "/./..w" -> 415 "/..w" +- "/./..a" -> 404 "/..a" +- "/./..w" -> 404 "/..w" - "/./..?" -> 200 "/" - "/./..%" -> 403 - "/.//.." -> 200 "/" -- "/.a../" -> 406 "/.a../" +- "/.a../" -> 404 "/.a../" - "/.a/.." -> 200 "/" -- "/.w../" -> 406 "/.w../" +- "/.w../" -> 404 "/.w../" - "/.w/.." -> 200 "/" -- "/.?../" -> 415 "/." +- "/.?../" -> 404 "/." - "/../.." -> 200 "/" - "/.%../" -> 403 - "/.%/.." -> 403 -- "//...." -> 415 "/...." -- "//.../" -> 406 "/.../" -- "//...a" -> 415 "/...a" -- "//...w" -> 415 "/...w" -- "//...?" -> 415 "/..." +- "//...." -> 404 "/...." +- "//.../" -> 404 "/.../" +- "//...a" -> 404 "/...a" +- "//...w" -> 404 "/...w" +- "//...?" -> 404 "/..." - "//...%" -> 403 - "//../." -> 200 "/" - "//..//" -> 200 "/" -- "//../a" -> 415 "/a" -- "//../w" -> 415 "/w" +- "//../a" -> 404 "/a" +- "//../w" -> 404 "/w" - "//../?" -> 200 "/" - "//../%" -> 403 -- "//..a." -> 415 "/..a." -- "//..a/" -> 406 "/..a/" -- "//..aa" -> 415 "/..aa" -- "//..aw" -> 415 "/..aw" -- "//..a?" -> 415 "/..a" +- "//..a." -> 404 "/..a." +- "//..a/" -> 404 "/..a/" +- "//..aa" -> 404 "/..aa" +- "//..aw" -> 404 "/..aw" +- "//..a?" -> 404 "/..a" - "//..a%" -> 403 -- "//..w." -> 415 "/..w." -- "//..w/" -> 406 "/..w/" -- "//..wa" -> 415 "/..wa" -- "//..ww" -> 415 "/..ww" -- "//..w?" -> 415 "/..w" +- "//..w." -> 404 "/..w." +- "//..w/" -> 404 "/..w/" +- "//..wa" -> 404 "/..wa" +- "//..ww" -> 404 "/..ww" +- "//..w?" -> 404 "/..w" - "//..w%" -> 403 - "//..?." -> 200 "/" - "//..?/" -> 200 "/" -- "//..?a" -> 415 "/a" -- "//..?w" -> 415 "/w" +- "//..?a" -> 404 "/a" +- "//..?w" -> 404 "/w" - "//..??" -> 200 "/" - "//..?%" -> 403 - "//..%." -> 403 @@ -660,65 +659,65 @@ cat </tmp/lwsresult1 - "//..%?" -> 403 - "//..%%" -> 403 - "//./.." -> 200 "/" -- "///..." -> 415 "/..." +- "///..." -> 404 "/..." - "///../" -> 200 "/" -- "///..a" -> 415 "/..a" -- "///..w" -> 415 "/..w" +- "///..a" -> 404 "/..a" +- "///..w" -> 404 "/..w" - "///..?" -> 200 "/" - "///..%" -> 403 - "////.." -> 200 "/" -- "//a../" -> 406 "/a../" +- "//a../" -> 404 "/a../" - "//a/.." -> 200 "/" -- "//w../" -> 406 "/w../" +- "//w../" -> 404 "/w../" - "//w/.." -> 200 "/" - "//?../" -> 200 "/" - "//?/.." -> 200 "/" - "//%../" -> 403 - "//%/.." -> 403 -- "/a.../" -> 406 "/a.../" -- "/a../." -> 406 "/a../" -- "/a..//" -> 406 "/a../" -- "/a../a" -> 406 "/a../a" -- "/a../w" -> 406 "/a../w" -- "/a../?" -> 406 "/a../" +- "/a.../" -> 404 "/a.../" +- "/a../." -> 404 "/a../" +- "/a..//" -> 404 "/a../" +- "/a../a" -> 404 "/a../a" +- "/a../w" -> 404 "/a../w" +- "/a../?" -> 404 "/a../" - "/a../%" -> 403 - "/a./.." -> 200 "/" -- "/a/..." -> 406 "/a/..." +- "/a/..." -> 404 "/a/..." - "/a/../" -> 200 "/" -- "/a/..a" -> 406 "/a/..a" -- "/a/..w" -> 406 "/a/..w" +- "/a/..a" -> 404 "/a/..a" +- "/a/..w" -> 404 "/a/..w" - "/a/..?" -> 200 "/" - "/a/..%" -> 403 - "/a//.." -> 200 "/" -- "/aa../" -> 406 "/aa../" +- "/aa../" -> 404 "/aa../" - "/aa/.." -> 200 "/" -- "/aw../" -> 406 "/aw../" +- "/aw../" -> 404 "/aw../" - "/aw/.." -> 200 "/" -- "/a?../" -> 415 "/a" -- "/a?/.." -> 415 "/a" +- "/a?../" -> 404 "/a" +- "/a?/.." -> 404 "/a" - "/a%../" -> 403 - "/a%/.." -> 403 -- "/w.../" -> 406 "/w.../" -- "/w../." -> 406 "/w../" -- "/w..//" -> 406 "/w../" -- "/w../a" -> 406 "/w../a" -- "/w../w" -> 406 "/w../w" -- "/w../?" -> 406 "/w../" +- "/w.../" -> 404 "/w.../" +- "/w../." -> 404 "/w../" +- "/w..//" -> 404 "/w../" +- "/w../a" -> 404 "/w../a" +- "/w../w" -> 404 "/w../w" +- "/w../?" -> 404 "/w../" - "/w../%" -> 403 - "/w./.." -> 200 "/" -- "/w/..." -> 406 "/w/..." +- "/w/..." -> 404 "/w/..." - "/w/../" -> 200 "/" -- "/w/..a" -> 406 "/w/..a" -- "/w/..w" -> 406 "/w/..w" +- "/w/..a" -> 404 "/w/..a" +- "/w/..w" -> 404 "/w/..w" - "/w/..?" -> 200 "/" - "/w/..%" -> 403 - "/w//.." -> 200 "/" -- "/wa../" -> 406 "/wa../" +- "/wa../" -> 404 "/wa../" - "/wa/.." -> 200 "/" -- "/ww../" -> 406 "/ww../" +- "/ww../" -> 404 "/ww../" - "/ww/.." -> 200 "/" -- "/w?../" -> 415 "/w" -- "/w?/.." -> 415 "/w" +- "/w?../" -> 404 "/w" +- "/w?/.." -> 404 "/w" - "/w%../" -> 403 - "/w%/.." -> 403 - "/?.../" -> 200 "/" @@ -767,13 +766,13 @@ cat </tmp/lwsresult1 - "/%?/.." -> 403 - "/%%../" -> 403 - "/%%/.." -> 403 -- "/a/w/../a" -> 406 "/a/a" -- "/path/to/dir/../other/dir" -> 406 "/path/to/other/dir" +- "/a/w/../a" -> 404 "/a/a" +- "/path/to/dir/../other/dir" -> 404 "/path/to/other/dir" EOF if [ "`md5sum /tmp/results | cut -d' ' -f 1`" != "`md5sum /tmp/lwsresult1 | cut -d' ' -f1`" ] ; then echo "Differences..." - diff -urN /tmp/results /tmp/lwsresult1 + diff -urN /tmp/lwsresult1 /tmp/results exit 1 else echo "OK" diff --git a/scripts/travis_control.sh b/scripts/travis_control.sh index 2eef3cf4d..a5fe7e677 100755 --- a/scripts/travis_control.sh +++ b/scripts/travis_control.sh @@ -14,7 +14,7 @@ else sudo make install && ../minimal-examples/selftests.sh && ../scripts/h2spec.sh && - ../test-apps/attack.sh && + ../scripts/attack.sh && ../scripts/h2load.sh && ../scripts/autobahn-test.sh else diff --git a/test-apps/test-server.c b/test-apps/test-server.c index f2cfc0d13..3ad1c6344 100644 --- a/test-apps/test-server.c +++ b/test-apps/test-server.c @@ -22,6 +22,7 @@ #include #include #include +#include int close_testing; int max_poll_elements; @@ -77,12 +78,69 @@ char crl_path[1024] = ""; #include "../plugins/protocol_post_demo.c" #endif +static int +lws_callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, + void *in, size_t len) +{ + const unsigned char *c; + char buf[1024]; + int n = 0, hlen; + + switch (reason) { + case LWS_CALLBACK_HTTP: + + /* non-mount-handled accesses will turn up here */ + + /* dump the headers */ + + do { + c = lws_token_to_string(n); + if (!c) { + n++; + continue; + } + + hlen = lws_hdr_total_length(wsi, n); + if (!hlen || hlen > (int)sizeof(buf) - 1) { + n++; + continue; + } + + lws_hdr_copy(wsi, buf, sizeof buf, n); + buf[sizeof(buf) - 1] = '\0'; + + fprintf(stderr, " %s = %s\n", (char *)c, buf); + n++; + } while (c); + + /* 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); + } + + if (lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL)) + return -1; + + if (lws_http_transaction_completed(wsi)) + return -1; + + return 0; + default: + break; + } + + return lws_callback_http_dummy(wsi, reason, user, in, len); +} + /* list of supported protocols and callbacks */ static struct lws_protocols protocols[] = { /* first protocol must always be HTTP handler */ - { "http-only", lws_callback_http_dummy, 0, 0, }, + { "http-only", lws_callback_http, 0, 0, }, #if defined(LWS_ROLE_WS) LWS_PLUGIN_PROTOCOL_DUMB_INCREMENT, LWS_PLUGIN_PROTOCOL_MIRROR,