1d4afb5ceSopenharmony_ci/* 2d4afb5ceSopenharmony_ci * lws-minimal-http-client-post 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 the a minimal http client using lws and POST. 10d4afb5ceSopenharmony_ci * 11d4afb5ceSopenharmony_ci * It POSTs both form data and a file to the form at 12d4afb5ceSopenharmony_ci * https://libwebsockets.org/testserver/formtest and dumps 13d4afb5ceSopenharmony_ci * the html page received generated by the POST handler. 14d4afb5ceSopenharmony_ci */ 15d4afb5ceSopenharmony_ci 16d4afb5ceSopenharmony_ci#include <libwebsockets.h> 17d4afb5ceSopenharmony_ci#include <string.h> 18d4afb5ceSopenharmony_ci#include <signal.h> 19d4afb5ceSopenharmony_ci 20d4afb5ceSopenharmony_cistatic int interrupted, bad = 0, status, count_clients = 1, completed; 21d4afb5ceSopenharmony_cistatic struct lws *client_wsi[4]; 22d4afb5ceSopenharmony_ci 23d4afb5ceSopenharmony_cistruct pss { 24d4afb5ceSopenharmony_ci char body_part; 25d4afb5ceSopenharmony_ci}; 26d4afb5ceSopenharmony_ci 27d4afb5ceSopenharmony_cistatic int 28d4afb5ceSopenharmony_cicallback_http(struct lws *wsi, enum lws_callback_reasons reason, 29d4afb5ceSopenharmony_ci void *user, void *in, size_t len) 30d4afb5ceSopenharmony_ci{ 31d4afb5ceSopenharmony_ci struct pss *pss = (struct pss *)user; 32d4afb5ceSopenharmony_ci char buf[LWS_PRE + 1024], *start = &buf[LWS_PRE], *p = start, 33d4afb5ceSopenharmony_ci *end = &buf[sizeof(buf) - 1]; 34d4afb5ceSopenharmony_ci int n; 35d4afb5ceSopenharmony_ci 36d4afb5ceSopenharmony_ci switch (reason) { 37d4afb5ceSopenharmony_ci 38d4afb5ceSopenharmony_ci /* because we are protocols[0] ... */ 39d4afb5ceSopenharmony_ci case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: 40d4afb5ceSopenharmony_ci lwsl_err("CLIENT_CONNECTION_ERROR: %s\n", 41d4afb5ceSopenharmony_ci in ? (char *)in : "(null)"); 42d4afb5ceSopenharmony_ci bad = 1; 43d4afb5ceSopenharmony_ci if (++completed == count_clients) 44d4afb5ceSopenharmony_ci lws_cancel_service(lws_get_context(wsi)); 45d4afb5ceSopenharmony_ci break; 46d4afb5ceSopenharmony_ci 47d4afb5ceSopenharmony_ci case LWS_CALLBACK_CLOSED_CLIENT_HTTP: 48d4afb5ceSopenharmony_ci for (n = 0; n < count_clients; n++) 49d4afb5ceSopenharmony_ci if (client_wsi[n] == wsi) { 50d4afb5ceSopenharmony_ci client_wsi[n] = NULL; 51d4afb5ceSopenharmony_ci bad |= status != 200; 52d4afb5ceSopenharmony_ci if (++completed == count_clients) 53d4afb5ceSopenharmony_ci /* abort poll wait */ 54d4afb5ceSopenharmony_ci lws_cancel_service(lws_get_context(wsi)); 55d4afb5ceSopenharmony_ci } 56d4afb5ceSopenharmony_ci break; 57d4afb5ceSopenharmony_ci 58d4afb5ceSopenharmony_ci /* ...callbacks related to receiving the result... */ 59d4afb5ceSopenharmony_ci 60d4afb5ceSopenharmony_ci case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: 61d4afb5ceSopenharmony_ci status = (int)lws_http_client_http_response(wsi); 62d4afb5ceSopenharmony_ci lwsl_user("Connected with server response: %d\n", status); 63d4afb5ceSopenharmony_ci break; 64d4afb5ceSopenharmony_ci 65d4afb5ceSopenharmony_ci case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ: 66d4afb5ceSopenharmony_ci lwsl_user("RECEIVE_CLIENT_HTTP_READ: read %d\n", (int)len); 67d4afb5ceSopenharmony_ci lwsl_hexdump_notice(in, len); 68d4afb5ceSopenharmony_ci return 0; /* don't passthru */ 69d4afb5ceSopenharmony_ci 70d4afb5ceSopenharmony_ci case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: 71d4afb5ceSopenharmony_ci n = sizeof(buf) - LWS_PRE; 72d4afb5ceSopenharmony_ci if (lws_http_client_read(wsi, &p, &n) < 0) 73d4afb5ceSopenharmony_ci return -1; 74d4afb5ceSopenharmony_ci 75d4afb5ceSopenharmony_ci return 0; /* don't passthru */ 76d4afb5ceSopenharmony_ci 77d4afb5ceSopenharmony_ci case LWS_CALLBACK_COMPLETED_CLIENT_HTTP: 78d4afb5ceSopenharmony_ci lwsl_user("LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n"); 79d4afb5ceSopenharmony_ci bad |= status != 200; 80d4afb5ceSopenharmony_ci /* 81d4afb5ceSopenharmony_ci * Do this to mark us as having processed the completion 82d4afb5ceSopenharmony_ci * so close doesn't duplicate (with pipelining, completion != 83d4afb5ceSopenharmony_ci * connection close 84d4afb5ceSopenharmony_ci */ 85d4afb5ceSopenharmony_ci for (n = 0; n < count_clients; n++) 86d4afb5ceSopenharmony_ci if (client_wsi[n] == wsi) 87d4afb5ceSopenharmony_ci client_wsi[n] = NULL; 88d4afb5ceSopenharmony_ci if (++completed == count_clients) 89d4afb5ceSopenharmony_ci /* abort poll wait */ 90d4afb5ceSopenharmony_ci lws_cancel_service(lws_get_context(wsi)); 91d4afb5ceSopenharmony_ci break; 92d4afb5ceSopenharmony_ci 93d4afb5ceSopenharmony_ci /* ...callbacks related to generating the POST... */ 94d4afb5ceSopenharmony_ci 95d4afb5ceSopenharmony_ci case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER: 96d4afb5ceSopenharmony_ci /* 97d4afb5ceSopenharmony_ci * Tell lws we are going to send the body next... 98d4afb5ceSopenharmony_ci */ 99d4afb5ceSopenharmony_ci if (!lws_http_is_redirected_to_get(wsi)) { 100d4afb5ceSopenharmony_ci lwsl_user("%s: doing POST flow\n", __func__); 101d4afb5ceSopenharmony_ci lws_client_http_body_pending(wsi, 1); 102d4afb5ceSopenharmony_ci lws_callback_on_writable(wsi); 103d4afb5ceSopenharmony_ci } else 104d4afb5ceSopenharmony_ci lwsl_user("%s: doing GET flow\n", __func__); 105d4afb5ceSopenharmony_ci break; 106d4afb5ceSopenharmony_ci 107d4afb5ceSopenharmony_ci case LWS_CALLBACK_CLIENT_HTTP_WRITEABLE: 108d4afb5ceSopenharmony_ci if (lws_http_is_redirected_to_get(wsi)) 109d4afb5ceSopenharmony_ci break; 110d4afb5ceSopenharmony_ci lwsl_user("LWS_CALLBACK_CLIENT_HTTP_WRITEABLE\n"); 111d4afb5ceSopenharmony_ci n = LWS_WRITE_HTTP; 112d4afb5ceSopenharmony_ci 113d4afb5ceSopenharmony_ci /* 114d4afb5ceSopenharmony_ci * For a small body like this, we could prepare it in memory and 115d4afb5ceSopenharmony_ci * send it all at once. But to show how to handle, eg, 116d4afb5ceSopenharmony_ci * arbitrary-sized file payloads, or huge form-data fields, the 117d4afb5ceSopenharmony_ci * sending is done in multiple passes through the event loop. 118d4afb5ceSopenharmony_ci */ 119d4afb5ceSopenharmony_ci 120d4afb5ceSopenharmony_ci switch (pss->body_part++) { 121d4afb5ceSopenharmony_ci case 0: 122d4afb5ceSopenharmony_ci if (lws_client_http_multipart(wsi, "text", NULL, NULL, 123d4afb5ceSopenharmony_ci &p, end)) 124d4afb5ceSopenharmony_ci return -1; 125d4afb5ceSopenharmony_ci /* notice every usage of the boundary starts with -- */ 126d4afb5ceSopenharmony_ci p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), "my text field\xd\xa"); 127d4afb5ceSopenharmony_ci break; 128d4afb5ceSopenharmony_ci case 1: 129d4afb5ceSopenharmony_ci if (lws_client_http_multipart(wsi, "file", "myfile.txt", 130d4afb5ceSopenharmony_ci "text/plain", &p, end)) 131d4afb5ceSopenharmony_ci return -1; 132d4afb5ceSopenharmony_ci p += lws_snprintf(p, lws_ptr_diff_size_t(end, p), 133d4afb5ceSopenharmony_ci "This is the contents of the " 134d4afb5ceSopenharmony_ci "uploaded file.\xd\xa" 135d4afb5ceSopenharmony_ci "\xd\xa"); 136d4afb5ceSopenharmony_ci break; 137d4afb5ceSopenharmony_ci case 2: 138d4afb5ceSopenharmony_ci if (lws_client_http_multipart(wsi, NULL, NULL, NULL, 139d4afb5ceSopenharmony_ci &p, end)) 140d4afb5ceSopenharmony_ci return -1; 141d4afb5ceSopenharmony_ci lws_client_http_body_pending(wsi, 0); 142d4afb5ceSopenharmony_ci /* necessary to support H2, it means we will write no 143d4afb5ceSopenharmony_ci * more on this stream */ 144d4afb5ceSopenharmony_ci n = LWS_WRITE_HTTP_FINAL; 145d4afb5ceSopenharmony_ci break; 146d4afb5ceSopenharmony_ci 147d4afb5ceSopenharmony_ci default: 148d4afb5ceSopenharmony_ci /* 149d4afb5ceSopenharmony_ci * We can get extra callbacks here, if nothing to do, 150d4afb5ceSopenharmony_ci * then do nothing. 151d4afb5ceSopenharmony_ci */ 152d4afb5ceSopenharmony_ci return 0; 153d4afb5ceSopenharmony_ci } 154d4afb5ceSopenharmony_ci 155d4afb5ceSopenharmony_ci if (lws_write(wsi, (uint8_t *)start, lws_ptr_diff_size_t(p, start), (enum lws_write_protocol)n) 156d4afb5ceSopenharmony_ci != lws_ptr_diff(p, start)) 157d4afb5ceSopenharmony_ci return 1; 158d4afb5ceSopenharmony_ci 159d4afb5ceSopenharmony_ci if (n != LWS_WRITE_HTTP_FINAL) 160d4afb5ceSopenharmony_ci lws_callback_on_writable(wsi); 161d4afb5ceSopenharmony_ci 162d4afb5ceSopenharmony_ci return 0; 163d4afb5ceSopenharmony_ci 164d4afb5ceSopenharmony_ci default: 165d4afb5ceSopenharmony_ci break; 166d4afb5ceSopenharmony_ci } 167d4afb5ceSopenharmony_ci 168d4afb5ceSopenharmony_ci return lws_callback_http_dummy(wsi, reason, user, in, len); 169d4afb5ceSopenharmony_ci} 170d4afb5ceSopenharmony_ci 171d4afb5ceSopenharmony_cistatic const struct lws_protocols protocols[] = { 172d4afb5ceSopenharmony_ci { 173d4afb5ceSopenharmony_ci "http", 174d4afb5ceSopenharmony_ci callback_http, 175d4afb5ceSopenharmony_ci sizeof(struct pss), 176d4afb5ceSopenharmony_ci 0, 0, NULL, 0 177d4afb5ceSopenharmony_ci }, 178d4afb5ceSopenharmony_ci LWS_PROTOCOL_LIST_TERM 179d4afb5ceSopenharmony_ci}; 180d4afb5ceSopenharmony_ci 181d4afb5ceSopenharmony_cistatic void 182d4afb5ceSopenharmony_cisigint_handler(int sig) 183d4afb5ceSopenharmony_ci{ 184d4afb5ceSopenharmony_ci interrupted = 1; 185d4afb5ceSopenharmony_ci} 186d4afb5ceSopenharmony_ci 187d4afb5ceSopenharmony_ciint main(int argc, const char **argv) 188d4afb5ceSopenharmony_ci{ 189d4afb5ceSopenharmony_ci struct lws_context_creation_info info; 190d4afb5ceSopenharmony_ci struct lws_client_connect_info i; 191d4afb5ceSopenharmony_ci struct lws_context *context; 192d4afb5ceSopenharmony_ci const char *p; 193d4afb5ceSopenharmony_ci int n = 0; 194d4afb5ceSopenharmony_ci 195d4afb5ceSopenharmony_ci signal(SIGINT, sigint_handler); 196d4afb5ceSopenharmony_ci 197d4afb5ceSopenharmony_ci memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */ 198d4afb5ceSopenharmony_ci lws_cmdline_option_handle_builtin(argc, argv, &info); 199d4afb5ceSopenharmony_ci lwsl_user("LWS minimal http client - POST [-d<verbosity>] [-l] [--h1]\n"); 200d4afb5ceSopenharmony_ci 201d4afb5ceSopenharmony_ci if (lws_cmdline_option(argc, argv, "-m")) 202d4afb5ceSopenharmony_ci count_clients = LWS_ARRAY_SIZE(client_wsi); 203d4afb5ceSopenharmony_ci 204d4afb5ceSopenharmony_ci info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; 205d4afb5ceSopenharmony_ci info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */ 206d4afb5ceSopenharmony_ci info.protocols = protocols; 207d4afb5ceSopenharmony_ci /* 208d4afb5ceSopenharmony_ci * since we know this lws context is only ever going to be used with 209d4afb5ceSopenharmony_ci * one client wsis / fds / sockets at a time, let lws know it doesn't 210d4afb5ceSopenharmony_ci * have to use the default allocations for fd tables up to ulimit -n. 211d4afb5ceSopenharmony_ci * It will just allocate for 1 internal and 1 (+ 1 http2 nwsi) that we 212d4afb5ceSopenharmony_ci * will use. 213d4afb5ceSopenharmony_ci */ 214d4afb5ceSopenharmony_ci info.fd_limit_per_thread = (unsigned int)(1 + count_clients + 1); 215d4afb5ceSopenharmony_ci 216d4afb5ceSopenharmony_ci#if defined(LWS_WITH_MBEDTLS) || defined(USE_WOLFSSL) 217d4afb5ceSopenharmony_ci /* 218d4afb5ceSopenharmony_ci * OpenSSL uses the system trust store. mbedTLS has to be told which 219d4afb5ceSopenharmony_ci * CA to trust explicitly. 220d4afb5ceSopenharmony_ci */ 221d4afb5ceSopenharmony_ci if (!lws_cmdline_option(argc, argv, "-l")) 222d4afb5ceSopenharmony_ci info.client_ssl_ca_filepath = "./libwebsockets.org.cer"; 223d4afb5ceSopenharmony_ci#endif 224d4afb5ceSopenharmony_ci 225d4afb5ceSopenharmony_ci context = lws_create_context(&info); 226d4afb5ceSopenharmony_ci if (!context) { 227d4afb5ceSopenharmony_ci lwsl_err("lws init failed\n"); 228d4afb5ceSopenharmony_ci return 1; 229d4afb5ceSopenharmony_ci } 230d4afb5ceSopenharmony_ci 231d4afb5ceSopenharmony_ci memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */ 232d4afb5ceSopenharmony_ci i.context = context; 233d4afb5ceSopenharmony_ci i.ssl_connection = LCCSCF_USE_SSL | LCCSCF_HTTP_MULTIPART_MIME; 234d4afb5ceSopenharmony_ci 235d4afb5ceSopenharmony_ci if (lws_cmdline_option(argc, argv, "-l")) { 236d4afb5ceSopenharmony_ci i.port = 7681; 237d4afb5ceSopenharmony_ci i.address = "localhost"; 238d4afb5ceSopenharmony_ci i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED; 239d4afb5ceSopenharmony_ci i.path = "/formtest"; 240d4afb5ceSopenharmony_ci } else { 241d4afb5ceSopenharmony_ci i.port = 443; 242d4afb5ceSopenharmony_ci i.address = "libwebsockets.org"; 243d4afb5ceSopenharmony_ci i.path = "/testserver/formtest"; 244d4afb5ceSopenharmony_ci } 245d4afb5ceSopenharmony_ci 246d4afb5ceSopenharmony_ci if (lws_cmdline_option(argc, argv, "--form1")) 247d4afb5ceSopenharmony_ci i.path = "/form1"; 248d4afb5ceSopenharmony_ci 249d4afb5ceSopenharmony_ci if ((p = lws_cmdline_option(argc, argv, "--port"))) 250d4afb5ceSopenharmony_ci i.port = atoi(p); 251d4afb5ceSopenharmony_ci 252d4afb5ceSopenharmony_ci i.host = i.address; 253d4afb5ceSopenharmony_ci i.origin = i.address; 254d4afb5ceSopenharmony_ci i.method = "POST"; 255d4afb5ceSopenharmony_ci 256d4afb5ceSopenharmony_ci /* force h1 even if h2 available */ 257d4afb5ceSopenharmony_ci if (lws_cmdline_option(argc, argv, "--h1")) 258d4afb5ceSopenharmony_ci i.alpn = "http/1.1"; 259d4afb5ceSopenharmony_ci 260d4afb5ceSopenharmony_ci i.protocol = protocols[0].name; 261d4afb5ceSopenharmony_ci 262d4afb5ceSopenharmony_ci for (n = 0; n < count_clients; n++) { 263d4afb5ceSopenharmony_ci i.pwsi = &client_wsi[n]; 264d4afb5ceSopenharmony_ci lwsl_notice("%s: connecting to %s:%d\n", __func__, 265d4afb5ceSopenharmony_ci i.address, i.port); 266d4afb5ceSopenharmony_ci if (!lws_client_connect_via_info(&i)) 267d4afb5ceSopenharmony_ci completed++; 268d4afb5ceSopenharmony_ci } 269d4afb5ceSopenharmony_ci 270d4afb5ceSopenharmony_ci while (n >= 0 && completed != count_clients && !interrupted) 271d4afb5ceSopenharmony_ci n = lws_service(context, 0); 272d4afb5ceSopenharmony_ci 273d4afb5ceSopenharmony_ci lws_context_destroy(context); 274d4afb5ceSopenharmony_ci lwsl_user("Completed: %s\n", bad ? "failed" : "OK"); 275d4afb5ceSopenharmony_ci 276d4afb5ceSopenharmony_ci return bad; 277d4afb5ceSopenharmony_ci} 278