1d4afb5ceSopenharmony_ci/* 2d4afb5ceSopenharmony_ci * lws-minimal-http-client-attach 3d4afb5ceSopenharmony_ci * 4d4afb5ceSopenharmony_ci * Written in 2010-2019 by Andy Green <andy@warmcat.com> 5d4afb5ceSopenharmony_ci * 6d4afb5ceSopenharmony_ci * This file is made available under the Creative Commons CC0 1.0 7d4afb5ceSopenharmony_ci * Universal Public Domain Dedication. 8d4afb5ceSopenharmony_ci * 9d4afb5ceSopenharmony_ci * This demonstrates how to use the lws_system (*attach) api to allow a 10d4afb5ceSopenharmony_ci * different thread to arrange to join an existing lws event loop safely. The 11d4afb5ceSopenharmony_ci * attached stuff does an http client GET from the lws event loop, even though 12d4afb5ceSopenharmony_ci * it was originally requested from a different thread than the lws event loop. 13d4afb5ceSopenharmony_ci */ 14d4afb5ceSopenharmony_ci 15d4afb5ceSopenharmony_ci#include <libwebsockets.h> 16d4afb5ceSopenharmony_ci#include <string.h> 17d4afb5ceSopenharmony_ci#include <signal.h> 18d4afb5ceSopenharmony_ci#if defined(WIN32) 19d4afb5ceSopenharmony_ci#define HAVE_STRUCT_TIMESPEC 20d4afb5ceSopenharmony_ci#if defined(pid_t) 21d4afb5ceSopenharmony_ci#undef pid_t 22d4afb5ceSopenharmony_ci#endif 23d4afb5ceSopenharmony_ci#endif 24d4afb5ceSopenharmony_ci#include <pthread.h> 25d4afb5ceSopenharmony_ci 26d4afb5ceSopenharmony_cistatic struct lws_context *context; 27d4afb5ceSopenharmony_cistatic pthread_t lws_thread; 28d4afb5ceSopenharmony_cistatic pthread_mutex_t lock; 29d4afb5ceSopenharmony_cistatic int interrupted, bad = 1, status; 30d4afb5ceSopenharmony_ci 31d4afb5ceSopenharmony_cistatic int 32d4afb5ceSopenharmony_cicallback_http(struct lws *wsi, enum lws_callback_reasons reason, 33d4afb5ceSopenharmony_ci void *user, void *in, size_t len) 34d4afb5ceSopenharmony_ci{ 35d4afb5ceSopenharmony_ci switch (reason) { 36d4afb5ceSopenharmony_ci 37d4afb5ceSopenharmony_ci /* because we are protocols[0] ... */ 38d4afb5ceSopenharmony_ci case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: 39d4afb5ceSopenharmony_ci lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", 40d4afb5ceSopenharmony_ci in ? (char *)in : "(null)"); 41d4afb5ceSopenharmony_ci interrupted = 1; 42d4afb5ceSopenharmony_ci break; 43d4afb5ceSopenharmony_ci 44d4afb5ceSopenharmony_ci case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: 45d4afb5ceSopenharmony_ci { 46d4afb5ceSopenharmony_ci char buf[128]; 47d4afb5ceSopenharmony_ci 48d4afb5ceSopenharmony_ci lws_get_peer_simple(wsi, buf, sizeof(buf)); 49d4afb5ceSopenharmony_ci status = (int)lws_http_client_http_response(wsi); 50d4afb5ceSopenharmony_ci 51d4afb5ceSopenharmony_ci lwsl_user("Connected to %s, http response: %d\n", 52d4afb5ceSopenharmony_ci buf, status); 53d4afb5ceSopenharmony_ci } 54d4afb5ceSopenharmony_ci break; 55d4afb5ceSopenharmony_ci 56d4afb5ceSopenharmony_ci /* chunks of chunked content, with header removed */ 57d4afb5ceSopenharmony_ci case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: 58d4afb5ceSopenharmony_ci lwsl_user("RECEIVE_CLIENT_HTTP_READ: read %d\n", (int)len); 59d4afb5ceSopenharmony_ci 60d4afb5ceSopenharmony_ci#if 0 /* enable to dump the html */ 61d4afb5ceSopenharmony_ci { 62d4afb5ceSopenharmony_ci const char *p = in; 63d4afb5ceSopenharmony_ci 64d4afb5ceSopenharmony_ci while (len--) 65d4afb5ceSopenharmony_ci if (*p < 0x7f) 66d4afb5ceSopenharmony_ci putchar(*p++); 67d4afb5ceSopenharmony_ci else 68d4afb5ceSopenharmony_ci putchar('.'); 69d4afb5ceSopenharmony_ci } 70d4afb5ceSopenharmony_ci#endif 71d4afb5ceSopenharmony_ci return 0; /* don't passthru */ 72d4afb5ceSopenharmony_ci 73d4afb5ceSopenharmony_ci /* uninterpreted http content */ 74d4afb5ceSopenharmony_ci case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: 75d4afb5ceSopenharmony_ci { 76d4afb5ceSopenharmony_ci char buffer[1024 + LWS_PRE]; 77d4afb5ceSopenharmony_ci char *px = buffer + LWS_PRE; 78d4afb5ceSopenharmony_ci int lenx = sizeof(buffer) - LWS_PRE; 79d4afb5ceSopenharmony_ci 80d4afb5ceSopenharmony_ci if (lws_http_client_read(wsi, &px, &lenx) < 0) 81d4afb5ceSopenharmony_ci return -1; 82d4afb5ceSopenharmony_ci } 83d4afb5ceSopenharmony_ci return 0; /* don't passthru */ 84d4afb5ceSopenharmony_ci 85d4afb5ceSopenharmony_ci case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: 86d4afb5ceSopenharmony_ci lwsl_user("LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n"); 87d4afb5ceSopenharmony_ci interrupted = 1; 88d4afb5ceSopenharmony_ci bad = status != 200; 89d4afb5ceSopenharmony_ci lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ 90d4afb5ceSopenharmony_ci break; 91d4afb5ceSopenharmony_ci 92d4afb5ceSopenharmony_ci case LWS_CALLBACK_CLOSED_CLIENT_HTTP: 93d4afb5ceSopenharmony_ci interrupted = 1; 94d4afb5ceSopenharmony_ci bad = status != 200; 95d4afb5ceSopenharmony_ci lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */ 96d4afb5ceSopenharmony_ci break; 97d4afb5ceSopenharmony_ci 98d4afb5ceSopenharmony_ci default: 99d4afb5ceSopenharmony_ci break; 100d4afb5ceSopenharmony_ci } 101d4afb5ceSopenharmony_ci 102d4afb5ceSopenharmony_ci return lws_callback_http_dummy(wsi, reason, user, in, len); 103d4afb5ceSopenharmony_ci} 104d4afb5ceSopenharmony_ci 105d4afb5ceSopenharmony_cistatic const struct lws_protocols protocols[] = { 106d4afb5ceSopenharmony_ci { 107d4afb5ceSopenharmony_ci "http", 108d4afb5ceSopenharmony_ci callback_http, 109d4afb5ceSopenharmony_ci 0, 0, 0, NULL, 0 110d4afb5ceSopenharmony_ci }, 111d4afb5ceSopenharmony_ci LWS_PROTOCOL_LIST_TERM 112d4afb5ceSopenharmony_ci}; 113d4afb5ceSopenharmony_ci 114d4afb5ceSopenharmony_civoid sigint_handler(int sig) 115d4afb5ceSopenharmony_ci{ 116d4afb5ceSopenharmony_ci interrupted = 1; 117d4afb5ceSopenharmony_ci} 118d4afb5ceSopenharmony_ci 119d4afb5ceSopenharmony_cistatic void 120d4afb5ceSopenharmony_ciattach_callback(struct lws_context *context, int tsi, void *opaque) 121d4afb5ceSopenharmony_ci{ 122d4afb5ceSopenharmony_ci struct lws_client_connect_info i; 123d4afb5ceSopenharmony_ci 124d4afb5ceSopenharmony_ci /* 125d4afb5ceSopenharmony_ci * Even though it was asked for from a different thread, we are called 126d4afb5ceSopenharmony_ci * back by lws from the lws event loop thread context 127d4afb5ceSopenharmony_ci * 128d4afb5ceSopenharmony_ci * We can set up our operations on the lws event loop and return so 129d4afb5ceSopenharmony_ci * they can happen asynchronously 130d4afb5ceSopenharmony_ci */ 131d4afb5ceSopenharmony_ci 132d4afb5ceSopenharmony_ci memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */ 133d4afb5ceSopenharmony_ci i.context = context; 134d4afb5ceSopenharmony_ci i.ssl_connection = LCCSCF_USE_SSL; 135d4afb5ceSopenharmony_ci i.ssl_connection |= LCCSCF_H2_QUIRK_OVERFLOWS_TXCR | 136d4afb5ceSopenharmony_ci LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM; 137d4afb5ceSopenharmony_ci i.port = 443; 138d4afb5ceSopenharmony_ci i.address = "warmcat.com"; 139d4afb5ceSopenharmony_ci i.path = "/"; 140d4afb5ceSopenharmony_ci i.host = i.address; 141d4afb5ceSopenharmony_ci i.origin = i.address; 142d4afb5ceSopenharmony_ci i.method = "GET"; 143d4afb5ceSopenharmony_ci 144d4afb5ceSopenharmony_ci i.protocol = protocols[0].name; 145d4afb5ceSopenharmony_ci 146d4afb5ceSopenharmony_ci lws_client_connect_via_info(&i); 147d4afb5ceSopenharmony_ci} 148d4afb5ceSopenharmony_ci 149d4afb5ceSopenharmony_ci 150d4afb5ceSopenharmony_cistatic int 151d4afb5ceSopenharmony_cilws_attach_with_pthreads_locking(struct lws_context *context, int tsi, 152d4afb5ceSopenharmony_ci lws_attach_cb_t cb, lws_system_states_t state, 153d4afb5ceSopenharmony_ci void *opaque, struct lws_attach_item **get) 154d4afb5ceSopenharmony_ci{ 155d4afb5ceSopenharmony_ci int n; 156d4afb5ceSopenharmony_ci 157d4afb5ceSopenharmony_ci pthread_mutex_lock(&lock); 158d4afb5ceSopenharmony_ci /* 159d4afb5ceSopenharmony_ci * We just provide system-specific locking around the lws non-threadsafe 160d4afb5ceSopenharmony_ci * helper that adds and removes things from the pt list 161d4afb5ceSopenharmony_ci */ 162d4afb5ceSopenharmony_ci n = __lws_system_attach(context, tsi, cb, state, opaque, get); 163d4afb5ceSopenharmony_ci pthread_mutex_unlock(&lock); 164d4afb5ceSopenharmony_ci 165d4afb5ceSopenharmony_ci return n; 166d4afb5ceSopenharmony_ci} 167d4afb5ceSopenharmony_ci 168d4afb5ceSopenharmony_ci 169d4afb5ceSopenharmony_cilws_system_ops_t ops = { 170d4afb5ceSopenharmony_ci .attach = lws_attach_with_pthreads_locking 171d4afb5ceSopenharmony_ci}; 172d4afb5ceSopenharmony_ci 173d4afb5ceSopenharmony_ci/* 174d4afb5ceSopenharmony_ci * We made this into a different thread to model it being run from completely 175d4afb5ceSopenharmony_ci * different codebase that's all linked together 176d4afb5ceSopenharmony_ci */ 177d4afb5ceSopenharmony_ci 178d4afb5ceSopenharmony_cistatic void * 179d4afb5ceSopenharmony_cilws_create(void *d) 180d4afb5ceSopenharmony_ci{ 181d4afb5ceSopenharmony_ci struct lws_context_creation_info info; 182d4afb5ceSopenharmony_ci 183d4afb5ceSopenharmony_ci lwsl_user("%s: tid %p\n", __func__, (void *)(intptr_t)pthread_self()); 184d4afb5ceSopenharmony_ci 185d4afb5ceSopenharmony_ci memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ 186d4afb5ceSopenharmony_ci info.port = CONTEXT_PORT_NO_LISTEN; 187d4afb5ceSopenharmony_ci info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; 188d4afb5ceSopenharmony_ci info.system_ops = &ops; 189d4afb5ceSopenharmony_ci info.protocols = protocols; 190d4afb5ceSopenharmony_ci 191d4afb5ceSopenharmony_ci context = lws_create_context(&info); 192d4afb5ceSopenharmony_ci if (!context) { 193d4afb5ceSopenharmony_ci lwsl_err("lws init failed\n"); 194d4afb5ceSopenharmony_ci goto bail; 195d4afb5ceSopenharmony_ci } 196d4afb5ceSopenharmony_ci 197d4afb5ceSopenharmony_ci /* start the event loop */ 198d4afb5ceSopenharmony_ci 199d4afb5ceSopenharmony_ci while (!interrupted) 200d4afb5ceSopenharmony_ci if (lws_service(context, 0)) 201d4afb5ceSopenharmony_ci interrupted = 1; 202d4afb5ceSopenharmony_ci 203d4afb5ceSopenharmony_ci lws_context_destroy(context); 204d4afb5ceSopenharmony_ci 205d4afb5ceSopenharmony_cibail: 206d4afb5ceSopenharmony_ci pthread_exit(NULL); 207d4afb5ceSopenharmony_ci 208d4afb5ceSopenharmony_ci return NULL; 209d4afb5ceSopenharmony_ci} 210d4afb5ceSopenharmony_ci 211d4afb5ceSopenharmony_ciint main(int argc, const char **argv) 212d4afb5ceSopenharmony_ci{ 213d4afb5ceSopenharmony_ci int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE; 214d4afb5ceSopenharmony_ci const char *p; 215d4afb5ceSopenharmony_ci void *retval; 216d4afb5ceSopenharmony_ci 217d4afb5ceSopenharmony_ci signal(SIGINT, sigint_handler); 218d4afb5ceSopenharmony_ci 219d4afb5ceSopenharmony_ci if ((p = lws_cmdline_option(argc, argv, "-d"))) 220d4afb5ceSopenharmony_ci logs = atoi(p); 221d4afb5ceSopenharmony_ci 222d4afb5ceSopenharmony_ci lws_set_log_level(logs, NULL); 223d4afb5ceSopenharmony_ci lwsl_user("LWS minimal http client attach\n"); 224d4afb5ceSopenharmony_ci 225d4afb5ceSopenharmony_ci pthread_mutex_init(&lock, NULL); 226d4afb5ceSopenharmony_ci 227d4afb5ceSopenharmony_ci /* 228d4afb5ceSopenharmony_ci * The idea of the example is we're going to split the lws context and 229d4afb5ceSopenharmony_ci * event loop off to be created from its own thread... this is like it 230d4afb5ceSopenharmony_ci * was actually started by some completely different code... 231d4afb5ceSopenharmony_ci */ 232d4afb5ceSopenharmony_ci 233d4afb5ceSopenharmony_ci if (pthread_create(&lws_thread, NULL, lws_create, NULL)) { 234d4afb5ceSopenharmony_ci lwsl_err("thread creation failed\n"); 235d4afb5ceSopenharmony_ci goto bail1; 236d4afb5ceSopenharmony_ci } 237d4afb5ceSopenharmony_ci 238d4afb5ceSopenharmony_ci /* 239d4afb5ceSopenharmony_ci * Now on the original / different thread representing a different 240d4afb5ceSopenharmony_ci * codebase that wants to join this existing event loop, we'll ask to 241d4afb5ceSopenharmony_ci * get a callback from the event loop context when the event loop 242d4afb5ceSopenharmony_ci * thread is operational. We have to wait around a bit because we 243d4afb5ceSopenharmony_ci * may run before the lws context was created. 244d4afb5ceSopenharmony_ci */ 245d4afb5ceSopenharmony_ci 246d4afb5ceSopenharmony_ci while (!context && n++ < 30) 247d4afb5ceSopenharmony_ci usleep(10000); 248d4afb5ceSopenharmony_ci 249d4afb5ceSopenharmony_ci if (!context) { 250d4afb5ceSopenharmony_ci lwsl_err("%s: context didn't start\n", __func__); 251d4afb5ceSopenharmony_ci goto bail; 252d4afb5ceSopenharmony_ci } 253d4afb5ceSopenharmony_ci 254d4afb5ceSopenharmony_ci /* 255d4afb5ceSopenharmony_ci * From our different, non event loop thread, ask for our attach 256d4afb5ceSopenharmony_ci * callback to get called when lws system state is OPERATIONAL 257d4afb5ceSopenharmony_ci */ 258d4afb5ceSopenharmony_ci 259d4afb5ceSopenharmony_ci lws_system_get_ops(context)->attach(context, 0, attach_callback, 260d4afb5ceSopenharmony_ci LWS_SYSTATE_OPERATIONAL, 261d4afb5ceSopenharmony_ci NULL, NULL); 262d4afb5ceSopenharmony_ci 263d4afb5ceSopenharmony_ci /* 264d4afb5ceSopenharmony_ci * That's all we wanted to do with our thread. Just wait for the lws 265d4afb5ceSopenharmony_ci * thread to exit as well. 266d4afb5ceSopenharmony_ci */ 267d4afb5ceSopenharmony_ci 268d4afb5ceSopenharmony_cibail: 269d4afb5ceSopenharmony_ci pthread_join(lws_thread, &retval); 270d4afb5ceSopenharmony_cibail1: 271d4afb5ceSopenharmony_ci pthread_mutex_destroy(&lock); 272d4afb5ceSopenharmony_ci 273d4afb5ceSopenharmony_ci lwsl_user("%s: finished\n", __func__); 274d4afb5ceSopenharmony_ci 275d4afb5ceSopenharmony_ci return 0; 276d4afb5ceSopenharmony_ci} 277