main.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. /*
  2. * lws-api-test-lws_sequencer
  3. *
  4. * Written in 2019 by Andy Green <[email protected]>
  5. *
  6. * This file is made available under the Creative Commons CC0 1.0
  7. * Universal Public Domain Dedication.
  8. *
  9. * This api test uses the lws_sequencer api to make five http client requests
  10. * to libwebsockets.org in sequence, from inside the event loop. The fourth
  11. * fourth http client request is directed to port 22 where it stalls
  12. * triggering the lws_sequencer timeout flow. The fifth is given a nonexistant
  13. * dns name and is expected to fail.
  14. */
  15. #include <libwebsockets.h>
  16. #include <signal.h>
  17. static int interrupted, test_good = 0;
  18. enum {
  19. SEQ1,
  20. SEQ2,
  21. SEQ3_404,
  22. SEQ4_TIMEOUT, /* we expect to timeout */
  23. SEQ5_BAD_ADDRESS /* we expect the connection to fail */
  24. };
  25. /*
  26. * This is the user defined struct whose space is allocated along with the
  27. * sequencer when that is created.
  28. *
  29. * You'd put everything your sequencer needs to do its job in here.
  30. */
  31. struct myseq {
  32. struct lws_vhost *vhost;
  33. struct lws *cwsi; /* client wsi for current step if any */
  34. int state; /* which test we're on */
  35. int http_resp;
  36. };
  37. /* sequencer messages specific to this sequencer */
  38. enum {
  39. SEQ_MSG_CLIENT_FAILED = LWSSEQ_USER_BASE,
  40. SEQ_MSG_CLIENT_DONE,
  41. };
  42. /* this is the sequence of GETs we will do */
  43. static const char *url_paths[] = {
  44. "https://libwebsockets.org/index.html",
  45. "https://libwebsockets.org/lws.css",
  46. "https://libwebsockets.org/404.html",
  47. "https://libwebsockets.org:22", /* this causes us to time out */
  48. "https://doesntexist.invalid/" /* fail early in connect */
  49. };
  50. static void
  51. sigint_handler(int sig)
  52. {
  53. interrupted = 1;
  54. }
  55. /*
  56. * This is the sequencer-aware http protocol handler. It monitors the client
  57. * http action and queues messages for the sequencer when something definitive
  58. * happens.
  59. */
  60. static int
  61. callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
  62. void *in, size_t len)
  63. {
  64. struct myseq *s = (struct myseq *)user;
  65. int seq_msg = SEQ_MSG_CLIENT_FAILED;
  66. switch (reason) {
  67. /* because we are protocols[0] ... */
  68. case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
  69. lwsl_notice("CLIENT_CONNECTION_ERROR: %s\n",
  70. in ? (char *)in : "(null)");
  71. goto notify;
  72. case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
  73. if (!s)
  74. return 1;
  75. s->http_resp = lws_http_client_http_response(wsi);
  76. lwsl_info("Connected with server response: %d\n", s->http_resp);
  77. break;
  78. /* chunks of chunked content, with header removed */
  79. case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ:
  80. lwsl_info("RECEIVE_CLIENT_HTTP_READ: read %d\n", (int)len);
  81. #if 0 /* enable to dump the html */
  82. {
  83. const char *p = in;
  84. while (len--)
  85. if (*p < 0x7f)
  86. putchar(*p++);
  87. else
  88. putchar('.');
  89. }
  90. #endif
  91. return 0; /* don't passthru */
  92. /* uninterpreted http content */
  93. case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
  94. {
  95. char buffer[1024 + LWS_PRE];
  96. char *px = buffer + LWS_PRE;
  97. int lenx = sizeof(buffer) - LWS_PRE;
  98. if (lws_http_client_read(wsi, &px, &lenx) < 0)
  99. return -1;
  100. }
  101. return 0; /* don't passthru */
  102. case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
  103. lwsl_notice("LWS_CALLBACK_COMPLETED_CLIENT_HTTP: wsi %p\n",
  104. wsi);
  105. if (!s)
  106. return 1;
  107. /*
  108. * We got a definitive transaction completion
  109. */
  110. seq_msg = SEQ_MSG_CLIENT_DONE;
  111. goto notify;
  112. case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
  113. lwsl_info("LWS_CALLBACK_CLOSED_CLIENT_HTTP\n");
  114. if (!s)
  115. return 1;
  116. lwsl_user("%s: wsi %p: seq failed at CLOSED_CLIENT_HTTP\n",
  117. __func__, wsi);
  118. goto notify;
  119. default:
  120. break;
  121. }
  122. return lws_callback_http_dummy(wsi, reason, user, in, len);
  123. notify:
  124. /*
  125. * We only inform the sequencer of a definitive outcome for our step.
  126. *
  127. * So once we have informed it, we detach ourselves from the sequencer
  128. * and the sequencer from ourselves. Wsi may want to live on but after
  129. * we got our result and moved on to the next test or completed, the
  130. * sequencer doesn't want to hear from it again.
  131. */
  132. if (!s)
  133. return 1;
  134. lws_set_wsi_user(wsi, NULL);
  135. s->cwsi = NULL;
  136. lws_seq_queue_event(lws_seq_from_user(s), seq_msg,
  137. NULL, NULL);
  138. return 0;
  139. }
  140. static const struct lws_protocols protocols[] = {
  141. { "seq-test-http", callback_http, 0, 0, },
  142. { NULL, NULL, 0, 0 }
  143. };
  144. static int
  145. sequencer_start_client(struct myseq *s)
  146. {
  147. struct lws_client_connect_info i;
  148. const char *prot, *path1;
  149. char uri[128], path[128];
  150. int n;
  151. lws_strncpy(uri, url_paths[s->state], sizeof(uri));
  152. memset(&i, 0, sizeof i);
  153. i.context = lws_seq_get_context(lws_seq_from_user(s));
  154. if (lws_parse_uri(uri, &prot, &i.address, &i.port, &path1)) {
  155. lwsl_err("%s: uri error %s\n", __func__, uri);
  156. }
  157. if (!strcmp(prot, "https"))
  158. i.ssl_connection = LCCSCF_USE_SSL;
  159. path[0] = '/';
  160. n = 1;
  161. if (path1[0] == '/')
  162. n = 0;
  163. lws_strncpy(&path[n], path1, sizeof(path) - 1);
  164. i.path = path;
  165. i.host = i.address;
  166. i.origin = i.address;
  167. i.method = "GET";
  168. i.vhost = s->vhost;
  169. i.userdata = s;
  170. i.protocol = protocols[0].name;
  171. i.local_protocol_name = protocols[0].name;
  172. i.pwsi = &s->cwsi;
  173. if (!lws_client_connect_via_info(&i)) {
  174. lwsl_notice("%s: connecting to %s://%s:%d%s failed\n",
  175. __func__, prot, i.address, i.port, path);
  176. /* we couldn't even get started with the client connection */
  177. lws_seq_queue_event(lws_seq_from_user(s),
  178. SEQ_MSG_CLIENT_FAILED, NULL, NULL);
  179. return 1;
  180. }
  181. lws_seq_timeout_us(lws_seq_from_user(s), 3 * LWS_US_PER_SEC);
  182. lwsl_notice("%s: wsi %p: connecting to %s://%s:%d%s\n", __func__,
  183. s->cwsi, prot, i.address, i.port, path);
  184. return 0;
  185. }
  186. /*
  187. * The sequencer callback handles queued sequencer messages in the order they
  188. * were queued. The messages are presented from the event loop thread context
  189. * even if they were queued from a different thread.
  190. */
  191. static lws_seq_cb_return_t
  192. sequencer_cb(struct lws_sequencer *seq, void *user, int event,
  193. void *data, void *aux)
  194. {
  195. struct myseq *s = (struct myseq *)user;
  196. switch ((int)event) {
  197. case LWSSEQ_CREATED: /* our sequencer just got started */
  198. s->state = SEQ1; /* first thing we'll do is the first url */
  199. goto step;
  200. case LWSSEQ_DESTROYED:
  201. /*
  202. * This sequencer is about to be destroyed. If we have any
  203. * other assets in play, detach them from us.
  204. */
  205. if (s->cwsi)
  206. lws_set_wsi_user(s->cwsi, NULL);
  207. interrupted = 1;
  208. break;
  209. case LWSSEQ_TIMED_OUT: /* current step timed out */
  210. if (s->state == SEQ4_TIMEOUT) {
  211. lwsl_user("%s: test %d got expected timeout\n",
  212. __func__, s->state);
  213. goto done;
  214. }
  215. lwsl_user("%s: seq timed out at step %d\n", __func__, s->state);
  216. return LWSSEQ_RET_DESTROY;
  217. case SEQ_MSG_CLIENT_FAILED:
  218. if (s->state == SEQ5_BAD_ADDRESS) {
  219. /*
  220. * in this specific case, we expect to fail
  221. */
  222. lwsl_user("%s: test %d failed as expected\n",
  223. __func__, s->state);
  224. goto done;
  225. }
  226. lwsl_user("%s: seq failed at step %d\n", __func__, s->state);
  227. return LWSSEQ_RET_DESTROY;
  228. case SEQ_MSG_CLIENT_DONE:
  229. if (s->state >= SEQ4_TIMEOUT) {
  230. /*
  231. * In these specific cases, done would be a failure,
  232. * we expected to timeout or fail
  233. */
  234. lwsl_user("%s: seq failed at step %d\n", __func__,
  235. s->state);
  236. return LWSSEQ_RET_DESTROY;
  237. }
  238. lwsl_user("%s: seq done step %d (resp %d)\n", __func__,
  239. s->state, s->http_resp);
  240. done:
  241. lws_seq_timeout_us(lws_seq_from_user(s), LWSSEQTO_NONE);
  242. s->state++;
  243. if (s->state == LWS_ARRAY_SIZE(url_paths)) {
  244. /* the sequence has completed */
  245. lwsl_user("%s: sequence completed OK\n", __func__);
  246. test_good = 1;
  247. return LWSSEQ_RET_DESTROY;
  248. }
  249. step:
  250. sequencer_start_client(s);
  251. break;
  252. default:
  253. break;
  254. }
  255. return LWSSEQ_RET_CONTINUE;
  256. }
  257. int
  258. main(int argc, const char **argv)
  259. {
  260. int n = 1, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
  261. struct lws_context_creation_info info;
  262. struct lws_context *context;
  263. struct lws_sequencer *seq;
  264. struct lws_vhost *vh;
  265. lws_seq_info_t i;
  266. struct myseq *s;
  267. const char *p;
  268. /* the normal lws init */
  269. signal(SIGINT, sigint_handler);
  270. if ((p = lws_cmdline_option(argc, argv, "-d")))
  271. logs = atoi(p);
  272. lws_set_log_level(logs, NULL);
  273. lwsl_user("LWS API selftest: lws_sequencer\n");
  274. memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
  275. info.port = CONTEXT_PORT_NO_LISTEN;
  276. info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT |
  277. LWS_SERVER_OPTION_EXPLICIT_VHOSTS;
  278. info.protocols = protocols;
  279. #if defined(LWS_WITH_MBEDTLS) || defined(USE_WOLFSSL)
  280. /*
  281. * OpenSSL uses the system trust store. mbedTLS has to be told which
  282. * CA to trust explicitly.
  283. */
  284. info.client_ssl_ca_filepath = "./libwebsockets.org.cer";
  285. #endif
  286. context = lws_create_context(&info);
  287. if (!context) {
  288. lwsl_err("lws init failed\n");
  289. return 1;
  290. }
  291. vh = lws_create_vhost(context, &info);
  292. if (!vh) {
  293. lwsl_err("Failed to create first vhost\n");
  294. goto bail1;
  295. }
  296. /*
  297. * Create the sequencer... when the event loop starts, it will
  298. * receive the LWSSEQ_CREATED callback
  299. */
  300. memset(&i, 0, sizeof(i));
  301. i.context = context;
  302. i.user_size = sizeof(struct myseq);
  303. i.puser = (void **)&s;
  304. i.cb = sequencer_cb;
  305. i.name = "seq";
  306. seq = lws_seq_create(&i);
  307. if (!seq) {
  308. lwsl_err("%s: unable to create sequencer\n", __func__);
  309. goto bail1;
  310. }
  311. s->vhost = vh;
  312. /* the usual lws event loop */
  313. while (n >= 0 && !interrupted)
  314. n = lws_service(context, 0);
  315. bail1:
  316. lwsl_user("Completed: %s\n", !test_good ? "FAIL" : "PASS");
  317. lws_context_destroy(context);
  318. return !test_good;
  319. }