aboutsummaryrefslogtreecommitdiff
path: root/minimal-examples/api-tests
diff options
context:
space:
mode:
authorAndy Green <andy@warmcat.com>2019-06-24 07:15:50 +0100
committerAndy Green <andy@warmcat.com>2019-06-25 12:10:18 +0100
commitb3d6e28bc7d662da18e7bab345e9fe935b9c4d88 (patch)
tree0bb52b0cf70db92f92e0d93524580e263c3a76f8 /minimal-examples/api-tests
parent477d50bf5614c5945bbb2b432f93339a20c5c96e (diff)
downloadlibwebsockets-b3d6e28bc7d662da18e7bab345e9fe935b9c4d88.tar.gz
lws_sequencer
Diffstat (limited to 'minimal-examples/api-tests')
-rw-r--r--minimal-examples/api-tests/api-test-lws_sequencer/CMakeLists.txt77
-rw-r--r--minimal-examples/api-tests/api-test-lws_sequencer/libwebsockets.org.cer92
-rw-r--r--minimal-examples/api-tests/api-test-lws_sequencer/main.c392
3 files changed, 561 insertions, 0 deletions
diff --git a/minimal-examples/api-tests/api-test-lws_sequencer/CMakeLists.txt b/minimal-examples/api-tests/api-test-lws_sequencer/CMakeLists.txt
new file mode 100644
index 00000000..cfb9b98e
--- /dev/null
+++ b/minimal-examples/api-tests/api-test-lws_sequencer/CMakeLists.txt
@@ -0,0 +1,77 @@
+cmake_minimum_required(VERSION 2.8)
+include(CheckCSourceCompiles)
+
+set(SAMP lws-api-test-lws_sequencer)
+set(SRCS main.c)
+
+# If we are being built as part of lws, confirm current build config supports
+# reqconfig, else skip building ourselves.
+#
+# If we are being built externally, confirm installed lws was configured to
+# support reqconfig, else error out with a helpful message about the problem.
+#
+MACRO(require_lws_config reqconfig _val result)
+
+ if (DEFINED ${reqconfig})
+ if (${reqconfig})
+ set (rq 1)
+ else()
+ set (rq 0)
+ endif()
+ else()
+ set(rq 0)
+ endif()
+
+ if (${_val} EQUAL ${rq})
+ set(SAME 1)
+ else()
+ set(SAME 0)
+ endif()
+
+ if (LWS_WITH_MINIMAL_EXAMPLES AND NOT ${SAME})
+ if (${_val})
+ message("${SAMP}: skipping as lws being built without ${reqconfig}")
+ else()
+ message("${SAMP}: skipping as lws built with ${reqconfig}")
+ endif()
+ set(${result} 0)
+ else()
+ if (LWS_WITH_MINIMAL_EXAMPLES)
+ set(MET ${SAME})
+ else()
+ CHECK_C_SOURCE_COMPILES("#include <libwebsockets.h>\nint main(void) {\n#if defined(${reqconfig})\n return 0;\n#else\n fail;\n#endif\n return 0;\n}\n" HAS_${reqconfig})
+ if (NOT DEFINED HAS_${reqconfig} OR NOT HAS_${reqconfig})
+ set(HAS_${reqconfig} 0)
+ else()
+ set(HAS_${reqconfig} 1)
+ endif()
+ if ((HAS_${reqconfig} AND ${_val}) OR (NOT HAS_${reqconfig} AND NOT ${_val}))
+ set(MET 1)
+ else()
+ set(MET 0)
+ endif()
+ endif()
+ if (NOT MET)
+ if (${_val})
+ message(FATAL_ERROR "This project requires lws must have been configured with ${reqconfig}")
+ else()
+ message(FATAL_ERROR "Lws configuration of ${reqconfig} is incompatible with this project")
+ endif()
+ endif()
+ endif()
+ENDMACRO()
+
+set(requirements 1)
+require_lws_config(LWS_ROLE_H1 1 requirements)
+require_lws_config(LWS_WITHOUT_CLIENT 0 requirements)
+
+if (requirements)
+ add_executable(${SAMP} ${SRCS})
+
+ if (websockets_shared)
+ target_link_libraries(${SAMP} websockets_shared)
+ add_dependencies(${SAMP} websockets_shared)
+ else()
+ target_link_libraries(${SAMP} websockets)
+ endif()
+endif()
diff --git a/minimal-examples/api-tests/api-test-lws_sequencer/libwebsockets.org.cer b/minimal-examples/api-tests/api-test-lws_sequencer/libwebsockets.org.cer
new file mode 100644
index 00000000..67de1292
--- /dev/null
+++ b/minimal-examples/api-tests/api-test-lws_sequencer/libwebsockets.org.cer
@@ -0,0 +1,92 @@
+-----BEGIN CERTIFICATE-----
+MIIGCDCCA/CgAwIBAgIQKy5u6tl1NmwUim7bo3yMBzANBgkqhkiG9w0BAQwFADCB
+hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G
+A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV
+BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTQwMjEy
+MDAwMDAwWhcNMjkwMjExMjM1OTU5WjCBkDELMAkGA1UEBhMCR0IxGzAZBgNVBAgT
+EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
+Q09NT0RPIENBIExpbWl0ZWQxNjA0BgNVBAMTLUNPTU9ETyBSU0EgRG9tYWluIFZh
+bGlkYXRpb24gU2VjdXJlIFNlcnZlciBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBAI7CAhnhoFmk6zg1jSz9AdDTScBkxwtiBUUWOqigwAwCfx3M28Sh
+bXcDow+G+eMGnD4LgYqbSRutA776S9uMIO3Vzl5ljj4Nr0zCsLdFXlIvNN5IJGS0
+Qa4Al/e+Z96e0HqnU4A7fK31llVvl0cKfIWLIpeNs4TgllfQcBhglo/uLQeTnaG6
+ytHNe+nEKpooIZFNb5JPJaXyejXdJtxGpdCsWTWM/06RQ1A/WZMebFEh7lgUq/51
+UHg+TLAchhP6a5i84DuUHoVS3AOTJBhuyydRReZw3iVDpA3hSqXttn7IzW3uLh0n
+c13cRTCAquOyQQuvvUSH2rnlG51/ruWFgqUCAwEAAaOCAWUwggFhMB8GA1UdIwQY
+MBaAFLuvfgI9+qbxPISOre44mOzZMjLUMB0GA1UdDgQWBBSQr2o6lFoL2JDqElZz
+30O0Oija5zAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNV
+HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwGwYDVR0gBBQwEjAGBgRVHSAAMAgG
+BmeBDAECATBMBgNVHR8ERTBDMEGgP6A9hjtodHRwOi8vY3JsLmNvbW9kb2NhLmNv
+bS9DT01PRE9SU0FDZXJ0aWZpY2F0aW9uQXV0aG9yaXR5LmNybDBxBggrBgEFBQcB
+AQRlMGMwOwYIKwYBBQUHMAKGL2h0dHA6Ly9jcnQuY29tb2RvY2EuY29tL0NPTU9E
+T1JTQUFkZFRydXN0Q0EuY3J0MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21v
+ZG9jYS5jb20wDQYJKoZIhvcNAQEMBQADggIBAE4rdk+SHGI2ibp3wScF9BzWRJ2p
+mj6q1WZmAT7qSeaiNbz69t2Vjpk1mA42GHWx3d1Qcnyu3HeIzg/3kCDKo2cuH1Z/
+e+FE6kKVxF0NAVBGFfKBiVlsit2M8RKhjTpCipj4SzR7JzsItG8kO3KdY3RYPBps
+P0/HEZrIqPW1N+8QRcZs2eBelSaz662jue5/DJpmNXMyYE7l3YphLG5SEXdoltMY
+dVEVABt0iN3hxzgEQyjpFv3ZBdRdRydg1vs4O2xyopT4Qhrf7W8GjEXCBgCq5Ojc
+2bXhc3js9iPc0d1sjhqPpepUfJa3w/5Vjo1JXvxku88+vZbrac2/4EjxYoIQ5QxG
+V/Iz2tDIY+3GH5QFlkoakdH368+PUq4NCNk+qKBR6cGHdNXJ93SrLlP7u3r7l+L4
+HyaPs9Kg4DdbKDsx5Q5XLVq4rXmsXiBmGqW5prU5wfWYQ//u+aen/e7KJD2AFsQX
+j4rBYKEMrltDR5FL1ZoXX/nUh8HCjLfn4g8wGTeGrODcQgPmlKidrv0PJFGUzpII
+0fxQ8ANAe4hZ7Q7drNJ3gjTcBpUC2JD5Leo31Rpg0Gcg19hCC0Wvgmje3WYkN5Ap
+lBlGGSW4gNfL1IYoakRwJiNiqZ+Gb7+6kHDSVneFeO/qJakXzlByjAA6quPbYzSf
++AZxAeKCINT+b72x
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFdDCCBFygAwIBAgIQJ2buVutJ846r13Ci/ITeIjANBgkqhkiG9w0BAQwFADBv
+MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
+ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF
+eHRlcm5hbCBDQSBSb290MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFow
+gYUxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
+BgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMSswKQYD
+VQQDEyJDT01PRE8gUlNBIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIICIjANBgkq
+hkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAkehUktIKVrGsDSTdxc9EZ3SZKzejfSNw
+AHG8U9/E+ioSj0t/EFa9n3Byt2F/yUsPF6c947AEYe7/EZfH9IY+Cvo+XPmT5jR6
+2RRr55yzhaCCenavcZDX7P0N+pxs+t+wgvQUfvm+xKYvT3+Zf7X8Z0NyvQwA1onr
+ayzT7Y+YHBSrfuXjbvzYqOSSJNpDa2K4Vf3qwbxstovzDo2a5JtsaZn4eEgwRdWt
+4Q08RWD8MpZRJ7xnw8outmvqRsfHIKCxH2XeSAi6pE6p8oNGN4Tr6MyBSENnTnIq
+m1y9TBsoilwie7SrmNnu4FGDwwlGTm0+mfqVF9p8M1dBPI1R7Qu2XK8sYxrfV8g/
+vOldxJuvRZnio1oktLqpVj3Pb6r/SVi+8Kj/9Lit6Tf7urj0Czr56ENCHonYhMsT
+8dm74YlguIwoVqwUHZwK53Hrzw7dPamWoUi9PPevtQ0iTMARgexWO/bTouJbt7IE
+IlKVgJNp6I5MZfGRAy1wdALqi2cVKWlSArvX31BqVUa/oKMoYX9w0MOiqiwhqkfO
+KJwGRXa/ghgntNWutMtQ5mv0TIZxMOmm3xaG4Nj/QN370EKIf6MzOi5cHkERgWPO
+GHFrK+ymircxXDpqR+DDeVnWIBqv8mqYqnK8V0rSS527EPywTEHl7R09XiidnMy/
+s1Hap0flhFMCAwEAAaOB9DCB8TAfBgNVHSMEGDAWgBStvZh6NLQm9/rEJlTvA73g
+JMtUGjAdBgNVHQ4EFgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQD
+AgGGMA8GA1UdEwEB/wQFMAMBAf8wEQYDVR0gBAowCDAGBgRVHSAAMEQGA1UdHwQ9
+MDswOaA3oDWGM2h0dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9BZGRUcnVzdEV4dGVy
+bmFsQ0FSb290LmNybDA1BggrBgEFBQcBAQQpMCcwJQYIKwYBBQUHMAGGGWh0dHA6
+Ly9vY3NwLnVzZXJ0cnVzdC5jb20wDQYJKoZIhvcNAQEMBQADggEBAGS/g/FfmoXQ
+zbihKVcN6Fr30ek+8nYEbvFScLsePP9NDXRqzIGCJdPDoCpdTPW6i6FtxFQJdcfj
+Jw5dhHk3QBN39bSsHNA7qxcS1u80GH4r6XnTq1dFDK8o+tDb5VCViLvfhVdpfZLY
+Uspzgb8c8+a4bmYRBbMelC1/kZWSWfFMzqORcUx8Rww7Cxn2obFshj5cqsQugsv5
+B5a6SE2Q8pTIqXOi6wZ7I53eovNNVZ96YUWYGGjHXkBrI/V5eu+MtWuLt29G9Hvx
+PUsE2JOAWVrgQSQdso8VYFhH2+9uRv0V9dlfmrPb2LjkQLPNlzmuhbsdjrzch5vR
+pu/xO28QOG8=
+-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU
+MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs
+IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290
+MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux
+FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h
+bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v
+dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt
+H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9
+uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX
+mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX
+a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN
+E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0
+WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD
+VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0
+Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU
+cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx
+IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN
+AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH
+YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5
+6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC
+Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX
+c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a
+mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
+-----END CERTIFICATE-----
diff --git a/minimal-examples/api-tests/api-test-lws_sequencer/main.c b/minimal-examples/api-tests/api-test-lws_sequencer/main.c
new file mode 100644
index 00000000..cbb3209d
--- /dev/null
+++ b/minimal-examples/api-tests/api-test-lws_sequencer/main.c
@@ -0,0 +1,392 @@
+/*
+ * lws-api-test-lws_sequencer
+ *
+ * Written in 2019 by Andy Green <andy@warmcat.com>
+ *
+ * This file is made available under the Creative Commons CC0 1.0
+ * Universal Public Domain Dedication.
+ *
+ * This api test uses the lws_sequencer api to make five http client requests
+ * to libwebsockets.org in sequence, from inside the event loop. The fourth
+ * fourth http client request is directed to port 22 where it stalls
+ * triggering the lws_sequencer timeout flow. The fifth is given a nonexistant
+ * dns name and is expected to fail.
+ */
+
+#include <libwebsockets.h>
+
+#include <signal.h>
+
+static int interrupted, test_good = 0;
+
+enum {
+ SEQ1,
+ SEQ2,
+ SEQ3_404,
+ SEQ4_TIMEOUT, /* we expect to timeout */
+ SEQ5_BAD_ADDRESS /* we expect the connection to fail */
+};
+
+/*
+ * This is the user defined struct whose space is allocated along with the
+ * sequencer when that is created.
+ *
+ * You'd put everything your sequencer needs to do its job in here.
+ */
+
+struct myseq {
+ struct lws_context *context;
+ struct lws_vhost *vhost;
+ struct lws *cwsi; /* client wsi for current step if any */
+
+ int state; /* which test we're on */
+ int http_resp;
+};
+
+/* sequencer messages specific to this sequencer */
+
+enum {
+ SEQ_MSG_CLIENT_FAILED = LWSSEQ_USER_BASE,
+ SEQ_MSG_CLIENT_DONE,
+};
+
+/* this is the sequence of GETs we will do */
+
+static const char *url_paths[] = {
+ "https://libwebsockets.org/index.html",
+ "https://libwebsockets.org/lws.css",
+ "https://libwebsockets.org/404.html",
+ "https://libwebsockets.org:22", /* this causes us to time out */
+ "https://doesntexist.invalid/" /* fail early in connect */
+};
+
+
+static void
+sigint_handler(int sig)
+{
+ interrupted = 1;
+}
+
+/*
+ * This is the sequencer-aware http protocol handler. It monitors the client
+ * http action and queues messages for the sequencer when something definitive
+ * happens.
+ */
+
+static int
+callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user,
+ void *in, size_t len)
+{
+ struct myseq *s = (struct myseq *)user;
+ int seq_msg = SEQ_MSG_CLIENT_FAILED;
+
+ switch (reason) {
+
+ /* because we are protocols[0] ... */
+ case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
+ lwsl_notice("CLIENT_CONNECTION_ERROR: %s\n",
+ in ? (char *)in : "(null)");
+ goto notify;
+
+ case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
+ if (!s)
+ return 1;
+ s->http_resp = lws_http_client_http_response(wsi);
+ lwsl_info("Connected with server response: %d\n", s->http_resp);
+ break;
+
+ /* chunks of chunked content, with header removed */
+ case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ:
+ lwsl_info("RECEIVE_CLIENT_HTTP_READ: read %d\n", (int)len);
+#if 0 /* enable to dump the html */
+ {
+ const char *p = in;
+
+ while (len--)
+ if (*p < 0x7f)
+ putchar(*p++);
+ else
+ putchar('.');
+ }
+#endif
+ return 0; /* don't passthru */
+
+ /* uninterpreted http content */
+ case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
+ {
+ char buffer[1024 + LWS_PRE];
+ char *px = buffer + LWS_PRE;
+ int lenx = sizeof(buffer) - LWS_PRE;
+
+ if (lws_http_client_read(wsi, &px, &lenx) < 0)
+ return -1;
+ }
+ return 0; /* don't passthru */
+
+ case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
+ lwsl_notice("LWS_CALLBACK_COMPLETED_CLIENT_HTTP: wsi %p\n",
+ wsi);
+ if (!s)
+ return 1;
+ /*
+ * We got a definitive transaction completion
+ */
+ seq_msg = SEQ_MSG_CLIENT_DONE;
+ goto notify;
+
+ case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
+ lwsl_info("LWS_CALLBACK_CLOSED_CLIENT_HTTP\n");
+ if (!s)
+ return 1;
+
+ lwsl_user("%s: wsi %p: seq failed at CLOSED_CLIENT_HTTP\n",
+ __func__, wsi);
+ goto notify;
+
+ default:
+ break;
+ }
+
+ return lws_callback_http_dummy(wsi, reason, user, in, len);
+
+notify:
+ /*
+ * We only inform the sequencer of a definitive outcome for our step.
+ *
+ * So once we have informed it, we detach ourselves from the sequencer
+ * and the sequencer from ourselves. Wsi may want to live on but after
+ * we got our result and moved on to the next test or completed, the
+ * sequencer doesn't want to hear from it again.
+ */
+ if (!s)
+ return 1;
+
+ lws_set_wsi_user(wsi, NULL);
+ s->cwsi = NULL;
+ lws_sequencer_event(lws_sequencer_from_user(s), seq_msg, NULL);
+
+ return 0;
+}
+
+static const struct lws_protocols protocols[] = {
+ { "seq-test-http", callback_http, 0, 0, },
+ { NULL, NULL, 0, 0 }
+};
+
+
+static int
+sequencer_start_client(struct myseq *s)
+{
+ struct lws_client_connect_info i;
+ const char *prot, *path1;
+ char uri[128], path[128];
+ int n;
+
+ lws_strncpy(uri, url_paths[s->state], sizeof(uri));
+
+ memset(&i, 0, sizeof i);
+ i.context = s->context;
+
+ if (lws_parse_uri(uri, &prot, &i.address, &i.port, &path1)) {
+ lwsl_err("%s: uri error %s\n", __func__, uri);
+ }
+
+ if (!strcmp(prot, "https"))
+ i.ssl_connection = LCCSCF_USE_SSL;
+
+ path[0] = '/';
+ n = 1;
+ if (path1[0] == '/')
+ n = 0;
+ lws_strncpy(&path[n], path1, sizeof(path) - 1);
+
+ i.path = path;
+ i.host = i.address;
+ i.origin = i.address;
+ i.method = "GET";
+ i.vhost = s->vhost;
+ i.userdata = s;
+
+ i.protocol = protocols[0].name;
+ i.local_protocol_name = protocols[0].name;
+ i.pwsi = &s->cwsi;
+
+ if (!lws_client_connect_via_info(&i)) {
+ lwsl_notice("%s: connecting to %s://%s:%d%s failed\n",
+ __func__, prot, i.address, i.port, path);
+
+ /* we couldn't even get started with the client connection */
+
+ lws_sequencer_event(lws_sequencer_from_user(s),
+ SEQ_MSG_CLIENT_FAILED, NULL);
+
+ return 1;
+ }
+
+ lws_sequencer_timeout(lws_sequencer_from_user(s), 3);
+
+ lwsl_notice("%s: wsi %p: connecting to %s://%s:%d%s\n", __func__,
+ s->cwsi, prot, i.address, i.port, path);
+
+ return 0;
+}
+
+/*
+ * The sequencer callback handles queued sequencer messages in the order they
+ * were queued. The messages are presented from the event loop thread context
+ * even if they were queued from a different thread.
+ */
+
+static lws_seq_cb_return_t
+sequencer_cb(struct lws_sequencer *seq, void *user, int event, void *data)
+{
+ struct myseq *s = (struct myseq *)user;
+
+ switch ((int)event) {
+ case LWSSEQ_CREATED: /* our sequencer just got started */
+ s->state = SEQ1; /* first thing we'll do is the first url */
+ goto step;
+
+ case LWSSEQ_DESTROYED:
+ /*
+ * This sequencer is about to be destroyed. If we have any
+ * other assets in play, detach them from us.
+ */
+ if (s->cwsi)
+ lws_set_wsi_user(s->cwsi, NULL);
+
+ interrupted = 1;
+ break;
+
+ case LWSSEQ_TIMED_OUT: /* current step timed out */
+ if (s->state == SEQ4_TIMEOUT) {
+ lwsl_user("%s: test %d got expected timeout\n",
+ __func__, s->state);
+ goto done;
+ }
+ lwsl_user("%s: seq timed out at step %d\n", __func__, s->state);
+ return LWSSEQ_RET_DESTROY;
+
+ case SEQ_MSG_CLIENT_FAILED:
+ if (s->state == SEQ5_BAD_ADDRESS) {
+ /*
+ * in this specific case, we expect to fail
+ */
+ lwsl_user("%s: test %d failed as expected\n",
+ __func__, s->state);
+ goto done;
+ }
+
+ lwsl_user("%s: seq failed at step %d\n", __func__, s->state);
+
+ return LWSSEQ_RET_DESTROY;
+
+ case SEQ_MSG_CLIENT_DONE:
+ if (s->state >= SEQ4_TIMEOUT) {
+ /*
+ * In these specific cases, done would be a failure,
+ * we expected to timeout or fail
+ */
+ lwsl_user("%s: seq failed at step %d\n", __func__,
+ s->state);
+
+ return LWSSEQ_RET_DESTROY;
+ }
+ lwsl_user("%s: seq done step %d (resp %d)\n", __func__,
+ s->state, s->http_resp);
+
+done:
+ lws_sequencer_timeout(lws_sequencer_from_user(s), 0);
+ s->state++;
+ if (s->state == LWS_ARRAY_SIZE(url_paths)) {
+ /* the sequence has completed */
+ lwsl_user("%s: sequence completed OK\n", __func__);
+
+ test_good = 1;
+
+ return LWSSEQ_RET_DESTROY;
+ }
+
+step:
+ sequencer_start_client(s);
+ break;
+ default:
+ break;
+ }
+
+ return LWSSEQ_RET_CONTINUE;
+}
+
+int
+main(int argc, const char **argv)
+{
+ int n = 1, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE;
+ struct lws_context_creation_info info;
+ struct lws_context *context;
+ lws_sequencer_t *seq;
+ struct lws_vhost *vh;
+ struct myseq *s;
+ const char *p;
+
+ /* the normal lws init */
+
+ signal(SIGINT, sigint_handler);
+
+ if ((p = lws_cmdline_option(argc, argv, "-d")))
+ logs = atoi(p);
+
+ lws_set_log_level(logs, NULL);
+ lwsl_user("LWS API selftest: lws_sequencer\n");
+
+ memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
+ info.port = CONTEXT_PORT_NO_LISTEN;
+ info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT |
+ LWS_SERVER_OPTION_EXPLICIT_VHOSTS;
+ info.protocols = protocols;
+
+#if defined(LWS_WITH_MBEDTLS)
+ /*
+ * OpenSSL uses the system trust store. mbedTLS has to be told which
+ * CA to trust explicitly.
+ */
+ info.client_ssl_ca_filepath = "./libwebsockets.org.cer";
+#endif
+
+ context = lws_create_context(&info);
+ if (!context) {
+ lwsl_err("lws init failed\n");
+ return 1;
+ }
+
+ vh = lws_create_vhost(context, &info);
+ if (!vh) {
+ lwsl_err("Failed to create first vhost\n");
+ goto bail1;
+ }
+
+ /*
+ * Create the sequencer... when the event loop starts, it will
+ * receive the LWSSEQ_CREATED callback
+ */
+
+ seq = lws_sequencer_create(context, 0, sizeof(struct myseq),
+ (void **)&s, sequencer_cb);
+ if (!seq) {
+ lwsl_err("%s: unable to create sequencer\n", __func__);
+ goto bail1;
+ }
+ s->context = context;
+ s->vhost = vh;
+
+ /* the usual lws event loop */
+
+ while (n >= 0 && !interrupted)
+ n = lws_service(context, 1000);
+
+bail1:
+ lwsl_user("Completed: %s\n", !test_good ? "FAIL" : "PASS");
+
+ lws_context_destroy(context);
+
+ return !test_good;
+}