/* * lws-minimal-http-server-eventlib-custom * * Written in 2010-2021 by Andy Green * * This file is made available under the Creative Commons CC0 1.0 * Universal Public Domain Dedication. * * This demonstrates a minimal http server using lws, on top of a custom "event * library" that uses an existing application POLL loop. * * To keep it simple, it serves stuff from the subdirectory "./mount-origin" of * the dir it was started in. Change mount.origin to serve from elsewhere. */ #include #include #include static int interrupted; static struct lws_context *context; #define MAX_CUSTOM_POLLFDS 64 /* this represents the existing application poll loop context we want lws * to cooperate with */ typedef struct custom_poll_ctx { struct lws_pollfd pollfds[MAX_CUSTOM_POLLFDS]; int count_pollfds; } custom_poll_ctx_t; /* for this example we just have the one, but it is passed into lws as a * foreign loop pointer, and all callbacks have access to it via that, so it * is not needed to be defined at file scope. */ static custom_poll_ctx_t a_cpcx; /* * These are the custom event loop operators that just make the custom event * loop able to work by itself. These would already exist in some form in an * existing application. */ static struct lws_pollfd * custom_poll_find_fd(custom_poll_ctx_t *cpcx, lws_sockfd_type fd) { int n; for (n = 0; n < cpcx->count_pollfds; n++) if (cpcx->pollfds[n].fd == fd) return &cpcx->pollfds[n]; return NULL; } static int custom_poll_add_fd(custom_poll_ctx_t *cpcx, lws_sockfd_type fd, int events) { struct lws_pollfd *pfd; lwsl_info("%s: ADD fd %d, ev %d\n", __func__, fd, events); pfd = custom_poll_find_fd(cpcx, fd); if (pfd) { lwsl_err("%s: ADD fd %d already in ext table\n", __func__, fd); return 1; } if (cpcx->count_pollfds == LWS_ARRAY_SIZE(cpcx->pollfds)) { lwsl_err("%s: no room left\n", __func__); return 1; } pfd = &cpcx->pollfds[cpcx->count_pollfds++]; pfd->fd = fd; pfd->events = (short)events; pfd->revents = 0; return 0; } static int custom_poll_del_fd(custom_poll_ctx_t *cpcx, lws_sockfd_type fd) { struct lws_pollfd *pfd; lwsl_info("%s: DEL fd %d\n", __func__, fd); pfd = custom_poll_find_fd(cpcx, fd); if (!pfd) { lwsl_err("%s: DEL fd %d missing in ext table\n", __func__, fd); return 1; } if (cpcx->count_pollfds > 1) *pfd = cpcx->pollfds[cpcx->count_pollfds - 1]; cpcx->count_pollfds--; return 0; } static int custom_poll_change_fd(custom_poll_ctx_t *cpcx, lws_sockfd_type fd, int events_add, int events_remove) { struct lws_pollfd *pfd; lwsl_info("%s: CHG fd %d, ev_add %d, ev_rem %d\n", __func__, fd, events_add, events_remove); pfd = custom_poll_find_fd(cpcx, fd); if (!pfd) return 1; pfd->events = (short)((pfd->events & (~events_remove)) | events_add); return 0; } int custom_poll_run(custom_poll_ctx_t *cpcx) { int n; while (!interrupted) { /* * Notice that the existing loop must consult with lws about * the maximum wait timeout to use. Lws will reduce the * timeout to the earliest scheduled event time if any earlier * than the provided timeout. */ n = lws_service_adjust_timeout(context, 5000, 0); lwsl_debug("%s: entering poll wait %dms\n", __func__, n); n = poll(cpcx->pollfds, (nfds_t)cpcx->count_pollfds, n); lwsl_debug("%s: exiting poll ret %d\n", __func__, n); if (n <= 0) continue; for (n = 0; n < cpcx->count_pollfds; n++) { int m; if (!cpcx->pollfds[n].revents) continue; m = lws_service_fd(context, &cpcx->pollfds[n]); if (m < 0) /* lws feels something bad happened, but * the outer application may not care */ continue; if (!m) { /* check if it is an fd owned by the * application */ } } } return 0; } /* * These is the custom "event library" interface layer between lws event lib * support and the custom loop implementation above. We only need to support * a few key apis. * * We are user code, so all the internal lws objects are opaque. But there are * enough public helpers to get everything done. */ /* one of these is appended to each pt for our use */ struct pt_eventlibs_custom { custom_poll_ctx_t *io_loop; }; /* * During lws context creation, we get called with the foreign loop pointer * that was passed in the creation info struct. Stash it in our private part * of the pt, so we can reference it in the other callbacks subsequently. */ static int init_pt_custom(struct lws_context *cx, void *_loop, int tsi) { struct pt_eventlibs_custom *priv = (struct pt_eventlibs_custom *) lws_evlib_tsi_to_evlib_pt(cx, tsi); /* store the loop we are bound to in our private part of the pt */ priv->io_loop = (custom_poll_ctx_t *)_loop; return 0; } static int sock_accept_custom(struct lws *wsi) { struct pt_eventlibs_custom *priv = (struct pt_eventlibs_custom *) lws_evlib_wsi_to_evlib_pt(wsi); return custom_poll_add_fd(priv->io_loop, lws_get_socket_fd(wsi), POLLIN); } static void io_custom(struct lws *wsi, unsigned int flags) { struct pt_eventlibs_custom *priv = (struct pt_eventlibs_custom *) lws_evlib_wsi_to_evlib_pt(wsi); int e_add = 0, e_remove = 0; if (flags & LWS_EV_START) { if (flags & LWS_EV_WRITE) e_add |= POLLOUT; if (flags & LWS_EV_READ) e_add |= POLLIN; } else { if (flags & LWS_EV_WRITE) e_remove |= POLLOUT; if (flags & LWS_EV_READ) e_remove |= POLLIN; } custom_poll_change_fd(priv->io_loop, lws_get_socket_fd(wsi), e_add, e_remove); } static int wsi_logical_close_custom(struct lws *wsi) { struct pt_eventlibs_custom *priv = (struct pt_eventlibs_custom *) lws_evlib_wsi_to_evlib_pt(wsi); return custom_poll_del_fd(priv->io_loop, lws_get_socket_fd(wsi)); } static const struct lws_event_loop_ops event_loop_ops_custom = { .name = "custom", .init_pt = init_pt_custom, .init_vhost_listen_wsi = sock_accept_custom, .sock_accept = sock_accept_custom, .io = io_custom, .wsi_logical_close = wsi_logical_close_custom, .evlib_size_pt = sizeof(struct pt_eventlibs_custom) }; static const lws_plugin_evlib_t evlib_custom = { .hdr = { "custom event loop", "lws_evlib_plugin", LWS_BUILD_HASH, LWS_PLUGIN_API_MAGIC }, .ops = &event_loop_ops_custom }; /* * The rest is just the normal minimal example for lws, with a couple of extra * lines wiring up the custom event library handlers above. */ 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 sigint_handler(int sig) { interrupted = 1; } int main(int argc, const char **argv) { struct lws_context_creation_info info; const char *p; int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; void *foreign_loops[1]; signal(SIGINT, sigint_handler); if ((p = lws_cmdline_option(argc, argv, "-d"))) logs = atoi(p); /* * init the existing custom event loop here if anything to do, don't * run it yet. In our example, no init required. */ lws_set_log_level(logs, NULL); lwsl_user("LWS minimal http server | visit http://localhost:7681\n"); 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_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; info.event_lib_custom = &evlib_custom; /* bind lws to our custom event * lib implementation above */ foreign_loops[0] = &a_cpcx; /* pass in the custom poll object as the * foreign loop object we will bind to */ info.foreign_loops = foreign_loops; context = lws_create_context(&info); if (!context) { lwsl_err("lws init failed\n"); return 1; } /* * We're going to run the custom loop now, instead of the lws loop. * We have told lws to cooperate with this loop to get stuff done. * * We only come back from this when interrupted gets set by SIGINT */ custom_poll_run(&a_cpcx); /* clean up lws part */ lws_context_destroy(context); return 0; }