1/* 2 * lws-minimal-ws-server 3 * 4 * Written in 2010-2019 by Andy Green <andy@warmcat.com> 5 * 6 * This file is made available under the Creative Commons CC0 1.0 7 * Universal Public Domain Dedication. 8 * 9 * This demonstrates a minimal ws server that can cooperate with 10 * other threads cleanly. Two other threads are started, which fill 11 * a ringbuffer with strings at 10Hz. 12 * 13 * The actual work and thread spawning etc are done in the protocol 14 * implementation in protocol_lws_minimal.c. 15 * 16 * To keep it simple, it serves stuff in the subdirectory "./mount-origin" of 17 * the directory it was started in. 18 * You can change that by changing mount.origin. 19 */ 20 21#include <libwebsockets.h> 22#include <string.h> 23#include <signal.h> 24#if defined(WIN32) 25#define HAVE_STRUCT_TIMESPEC 26#if defined(pid_t) 27#undef pid_t 28#endif 29#endif 30#include <pthread.h> 31 32#define LWS_PLUGIN_STATIC 33#include "protocol_lws_minimal.c" 34 35#define COUNT_THREADS 2 36 37static struct lws_protocols protocols[] = { 38 { "http", lws_callback_http_dummy, 0, 0, 0, NULL, 0 }, 39 LWS_PLUGIN_PROTOCOL_MINIMAL, 40 LWS_PROTOCOL_LIST_TERM 41}; 42 43static struct lws_context *context; 44static int interrupted, started; 45static pthread_t pthread_service[COUNT_THREADS]; 46 47static const struct lws_http_mount mount = { 48 /* .mount_next */ NULL, /* linked-list "next" */ 49 /* .mountpoint */ "/", /* mountpoint URL */ 50 /* .origin */ "./mount-origin", /* serve from dir */ 51 /* .def */ "index.html", /* default filename */ 52 /* .protocol */ NULL, 53 /* .cgienv */ NULL, 54 /* .extra_mimetypes */ NULL, 55 /* .interpret */ NULL, 56 /* .cgi_timeout */ 0, 57 /* .cache_max_age */ 0, 58 /* .auth_mask */ 0, 59 /* .cache_reusable */ 0, 60 /* .cache_revalidate */ 0, 61 /* .cache_intermediaries */ 0, 62 /* .origin_protocol */ LWSMPRO_FILE, /* files in a dir */ 63 /* .mountpoint_len */ 1, /* char count */ 64 /* .basic_auth_login_file */ NULL, 65}; 66 67/* 68 * This demonstrates how to pass a pointer into a specific protocol handler 69 * running on a specific vhost. In this case, it's our default vhost and 70 * we pass the pvo named "config" with the value a const char * "myconfig". 71 * 72 * This is the preferred way to pass configuration into a specific vhost + 73 * protocol instance. 74 */ 75 76static const struct lws_protocol_vhost_options pvo_ops = { 77 NULL, 78 NULL, 79 "config", /* pvo name */ 80 (void *)"myconfig" /* pvo value */ 81}; 82 83static const struct lws_protocol_vhost_options pvo = { 84 NULL, /* "next" pvo linked-list */ 85 &pvo_ops, /* "child" pvo linked-list */ 86 "lws-minimal", /* protocol name we belong to on this vhost */ 87 "" /* ignored */ 88}; 89 90void *thread_service(void *threadid) 91{ 92 while (lws_service_tsi(context, 1000, 93 (int)(lws_intptr_t)threadid) >= 0 && 94 !interrupted) 95 ; 96 97 pthread_exit(NULL); 98 99 return NULL; 100} 101 102static int 103system_notify_cb(lws_state_manager_t *mgr, lws_state_notify_link_t *link, 104 int current, int target) 105{ 106 struct lws_context *context = mgr->parent; 107 void *retval; 108 109 if (current != target) 110 return 0; 111 112 switch (current) { 113 case LWS_SYSTATE_OPERATIONAL: 114 lwsl_notice(" Service threads: %d\n", 115 lws_get_count_threads(context)); 116 117 /* start all the service threads */ 118 119 for (started = 1; started < lws_get_count_threads(context); 120 started++) 121 if (pthread_create(&pthread_service[started], NULL, 122 thread_service, 123 (void *)(lws_intptr_t)started)) 124 lwsl_err("Failed to start service thread\n"); 125 break; 126 case LWS_SYSTATE_CONTEXT_DESTROYING: 127 /* wait for all the service threads to exit */ 128 129 while ((--started) >= 1) 130 pthread_join(pthread_service[started], &retval); 131 132 break; 133 } 134 135 return 0; 136} 137 138lws_state_notify_link_t notifier = { { NULL, NULL, NULL }, 139 system_notify_cb, "app" }; 140lws_state_notify_link_t *na[] = { ¬ifier, NULL }; 141 142void sigint_handler(int sig) 143{ 144 interrupted = 1; 145 lws_cancel_service(context); 146} 147 148int main(int argc, const char **argv) 149{ 150 int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; 151 struct lws_context_creation_info info; 152 const char *p; 153 int n = 0; 154 155 signal(SIGINT, sigint_handler); 156 157 if ((p = lws_cmdline_option(argc, argv, "-d"))) 158 logs = atoi(p); 159 160 lws_set_log_level(logs, NULL); 161 lwsl_user("LWS minimal ws server + threads + smp | visit http://localhost:7681\n"); 162 163 memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ 164 info.port = 7681; 165 info.mounts = &mount; 166 info.protocols = protocols; 167 info.pvo = &pvo; /* per-vhost options */ 168 info.count_threads = COUNT_THREADS; 169 info.register_notifier_list = na; 170 info.options = 171 LWS_SERVER_OPTION_HTTP_HEADERS_SECURITY_BEST_PRACTICES_ENFORCE; 172 173 context = lws_create_context(&info); 174 if (!context) { 175 lwsl_err("lws init failed\n"); 176 return 1; 177 } 178 179 while (n >= 0 && !interrupted) 180 n = lws_service(context, 0); 181 182 lws_context_destroy(context); 183 184 return 0; 185} 186