Project homepage Mailing List  Warmcat.com  API Docs  Github Mirror 
{"schema":"libjg2-1", "vpath":"/git/", "avatar":"/git/avatar/", "alang":"en-US,en;q\u003d0.5", "gen_ut":1594301272, "reponame":"libwebsockets", "desc":"libwebsockets lightweight C networking library", "owner": { "name": "Andy Green", "email": "andy@warmcat.com", "md5": "c50933ca2aa61e0fe2c43d46bb6b59cb" },"url":"https://libwebsockets.org/repo/libwebsockets", "f":3, "items": [ {"schema":"libjg2-1", "cid":"72302892a87c0dfb941e57c4f8b87d53", "oid":{ "oid": "07aac9d6796d2d7595bcf1a907ab36341ef7335f", "alias": [ "refs/heads/master"]},"blobname": "minimal-examples/http-client/minimal-http-client-multi/minimal-http-client-multi.c", "blob": "/*\n * lws-minimal-http-client-multi\n *\n * Written in 2010-2020 by Andy Green \u003candy@warmcat.com\u003e\n *\n * This file is made available under the Creative Commons CC0 1.0\n * Universal Public Domain Dedication.\n *\n * This demonstrates the a minimal http client using lws, which makes\n * 8 downloads simultaneously from warmcat.com.\n *\n * Currently that takes the form of 8 individual simultaneous tcp and\n * tls connections, which happen concurrently. Notice that the ordering\n * of the returned payload may be intermingled for the various connections.\n *\n * By default the connections happen all together at the beginning and operate\n * concurrently, which is fast. However this is resource-intenstive, there are\n * 8 tcp connections, 8 tls tunnels on both the client and server. You can\n * instead opt to have the connections happen one after the other inside a\n * single tcp connection and tls tunnel, using HTTP/1.1 pipelining. To be\n * eligible to be pipelined on another existing connection to the same server,\n * the client connection must have the LCCSCF_PIPELINE flag on its\n * info.ssl_connection member (this is independent of whether the connection\n * is in ssl mode or not).\n *\n * HTTP/1.0: Pipelining only possible if Keep-Alive: yes sent by server\n * HTTP/1.1: always possible... serializes requests\n * HTTP/2: always possible... all requests sent as individual streams in parallel\n */\n\n#include \u003clibwebsockets.h\u003e\n#include \u003cstring.h\u003e\n#include \u003csignal.h\u003e\n#include \u003cassert.h\u003e\n#include \u003ctime.h\u003e\n\n#define COUNT 8\n\nstruct cliuser {\n\tint index;\n};\n\nstatic int completed, failed, numbered, stagger_idx, posting, count \u003d COUNT;\nstatic lws_sorted_usec_list_t sul_stagger;\nstatic struct lws_client_connect_info i;\nstatic struct lws *client_wsi[COUNT];\nstatic char urlpath[64], intr;\nstatic struct lws_context *context;\n\n/* we only need this for tracking POST emit state */\n\nstruct pss {\n\tchar body_part;\n};\n\nstatic int\ncallback_http(struct lws *wsi, enum lws_callback_reasons reason,\n\t void *user, void *in, size_t len)\n{\n\tchar buf[LWS_PRE + 1024], *start \u003d \u0026buf[LWS_PRE], *p \u003d start,\n\t *end \u003d \u0026buf[sizeof(buf) - LWS_PRE - 1];\n\tint n, idx \u003d (int)(intptr_t)lws_get_opaque_user_data(wsi);\n\tstruct pss *pss \u003d (struct pss *)user;\n\n\tswitch (reason) {\n\n\tcase LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:\n\t\tlwsl_user(\u0022LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP: idx: %d, resp %u\u005cn\u0022,\n\t\t\t\tidx, lws_http_client_http_response(wsi));\n\t\tbreak;\n\n\t/* because we are protocols[0] ... */\n\tcase LWS_CALLBACK_CLIENT_CONNECTION_ERROR:\n\t\tlwsl_err(\u0022CLIENT_CONNECTION_ERROR: %s\u005cn\u0022,\n\t\t\t in ? (char *)in : \u0022(null)\u0022);\n\t\tclient_wsi[idx] \u003d NULL;\n\t\tfailed++;\n\t\tgoto finished;\n\n\t/* chunks of chunked content, with header removed */\n\tcase LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ:\n\t\tlwsl_user(\u0022RECEIVE_CLIENT_HTTP_READ: conn %d: read %d\u005cn\u0022, idx, (int)len);\n\t\tlwsl_hexdump_info(in, len);\n\t\treturn 0; /* don't passthru */\n\n\tcase LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:\n\t\t/*\n\t\t * Tell lws we are going to send the body next...\n\t\t */\n\t\tif (posting \u0026\u0026 !lws_http_is_redirected_to_get(wsi)) {\n\t\t\tlwsl_user(\u0022%s: doing POST flow\u005cn\u0022, __func__);\n\t\t\tlws_client_http_body_pending(wsi, 1);\n\t\t\tlws_callback_on_writable(wsi);\n\t\t} else\n\t\t\tlwsl_user(\u0022%s: doing GET flow\u005cn\u0022, __func__);\n\t\tbreak;\n\n\t/* uninterpreted http content */\n\tcase LWS_CALLBACK_RECEIVE_CLIENT_HTTP:\n\t\t{\n\t\t\tchar buffer[1024 + LWS_PRE];\n\t\t\tchar *px \u003d buffer + LWS_PRE;\n\t\t\tint lenx \u003d sizeof(buffer) - LWS_PRE;\n\n\t\t\tif (lws_http_client_read(wsi, \u0026px, \u0026lenx) \u003c 0)\n\t\t\t\treturn -1;\n\t\t}\n\t\treturn 0; /* don't passthru */\n\n\tcase LWS_CALLBACK_COMPLETED_CLIENT_HTTP:\n\t\tlwsl_user(\u0022LWS_CALLBACK_COMPLETED_CLIENT_HTTP %p: idx %d\u005cn\u0022,\n\t\t\t wsi, idx);\n\t\tclient_wsi[idx] \u003d NULL;\n\t\tgoto finished;\n\n\tcase LWS_CALLBACK_CLOSED_CLIENT_HTTP:\n\t\tlwsl_info(\u0022%s: closed: %p\u005cn\u0022, __func__, client_wsi[idx]);\n\t\tif (client_wsi[idx]) {\n\t\t\t/*\n\t\t\t * If it completed normally, it will have been set to\n\t\t\t * NULL then already. So we are dealing with an\n\t\t\t * abnormal, failing, close\n\t\t\t */\n\t\t\tclient_wsi[idx] \u003d NULL;\n\t\t\tfailed++;\n\t\t\tgoto finished;\n\t\t}\n\t\tbreak;\n\n\tcase LWS_CALLBACK_CLIENT_HTTP_WRITEABLE:\n\t\tif (!posting)\n\t\t\tbreak;\n\t\tif (lws_http_is_redirected_to_get(wsi))\n\t\t\tbreak;\n\t\tlwsl_info(\u0022LWS_CALLBACK_CLIENT_HTTP_WRITEABLE: %p, idx %d,\u0022\n\t\t\t\t\u0022 part %d\u005cn\u0022, wsi, idx, pss-\u003ebody_part);\n\n\t\tn \u003d LWS_WRITE_HTTP;\n\n\t\t/*\n\t\t * For a small body like this, we could prepare it in memory and\n\t\t * send it all at once. But to show how to handle, eg,\n\t\t * arbitrary-sized file payloads, or huge form-data fields, the\n\t\t * sending is done in multiple passes through the event loop.\n\t\t */\n\n\t\tswitch (pss-\u003ebody_part++) {\n\t\tcase 0:\n\t\t\tif (lws_client_http_multipart(wsi, \u0022text\u0022, NULL, NULL,\n\t\t\t\t\t\t \u0026p, end))\n\t\t\t\treturn -1;\n\t\t\t/* notice every usage of the boundary starts with -- */\n\t\t\tp +\u003d lws_snprintf(p, end - p, \u0022my text field\u005cxd\u005cxa\u0022);\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tif (lws_client_http_multipart(wsi, \u0022file\u0022, \u0022myfile.txt\u0022,\n\t\t\t\t\t\t \u0022text/plain\u0022, \u0026p, end))\n\t\t\t\treturn -1;\n\t\t\tp +\u003d lws_snprintf(p, end - p,\n\t\t\t\t\t\u0022This is the contents of the \u0022\n\t\t\t\t\t\u0022uploaded file.\u005cxd\u005cxa\u0022\n\t\t\t\t\t\u0022\u005cxd\u005cxa\u0022);\n\t\t\tbreak;\n\t\tcase 2:\n\t\t\tif (lws_client_http_multipart(wsi, NULL, NULL, NULL,\n\t\t\t\t\t\t \u0026p, end))\n\t\t\t\treturn -1;\n\t\t\tlws_client_http_body_pending(wsi, 0);\n\t\t\t /* necessary to support H2, it means we will write no\n\t\t\t * more on this stream */\n\t\t\tn \u003d LWS_WRITE_HTTP_FINAL;\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\t/*\n\t\t\t * We can get extra callbacks here, if nothing to do,\n\t\t\t * then do nothing.\n\t\t\t */\n\t\t\treturn 0;\n\t\t}\n\n\t\tif (lws_write(wsi, (uint8_t *)start, lws_ptr_diff(p, start), n)\n\t\t\t\t!\u003d lws_ptr_diff(p, start))\n\t\t\treturn 1;\n\n\t\tif (n !\u003d LWS_WRITE_HTTP_FINAL)\n\t\t\tlws_callback_on_writable(wsi);\n\n\t\tbreak;\n\n\tdefault:\n\t\tbreak;\n\t}\n\n\treturn lws_callback_http_dummy(wsi, reason, user, in, len);\n\nfinished:\n\tif (++completed \u003d\u003d count) {\n\t\tif (!failed)\n\t\t\tlwsl_user(\u0022Done: all OK\u005cn\u0022);\n\t\telse\n\t\t\tlwsl_err(\u0022Done: failed: %d\u005cn\u0022, failed);\n\t\tintr \u003d 1;\n\t\t/*\n\t\t * This is how we can exit the event loop even when it's an\n\t\t * event library backing it... it will start and stage the\n\t\t * destroy to happen after we exited this service for each pt\n\t\t */\n\t\tlws_context_destroy(lws_get_context(wsi));\n\t}\n\n\treturn 0;\n}\n\nstatic const struct lws_protocols protocols[] \u003d {\n\t{ \u0022http\u0022, callback_http, sizeof(struct pss), 0, },\n\t{ NULL, NULL, 0, 0 }\n};\n\nstatic void\nsignal_cb(void *handle, int signum)\n{\n\tswitch (signum) {\n\tcase SIGTERM:\n\tcase SIGINT:\n\t\tbreak;\n\tdefault:\n\t\tlwsl_err(\u0022%s: signal %d\u005cn\u0022, __func__, signum);\n\t\tbreak;\n\t}\n\tlws_context_destroy(context);\n}\n\nstatic void\nsigint_handler(int sig)\n{\n\tsignal_cb(NULL, sig);\n}\n\n#if defined(WIN32)\nint gettimeofday(struct timeval * tp, struct timezone * tzp)\n{\n // Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's\n // This magic number is the number of 100 nanosecond intervals since January 1, 1601 (UTC)\n // until 00:00:00 January 1, 1970 \n static const uint64_t EPOCH \u003d ((uint64_t) 116444736000000000ULL);\n\n SYSTEMTIME system_time;\n FILETIME file_time;\n uint64_t time;\n\n GetSystemTime( \u0026system_time );\n SystemTimeToFileTime( \u0026system_time, \u0026file_time );\n time \u003d ((uint64_t)file_time.dwLowDateTime ) ;\n time +\u003d ((uint64_t)file_time.dwHighDateTime) \u003c\u003c 32;\n\n tp-\u003etv_sec \u003d (long) ((time - EPOCH) / 10000000L);\n tp-\u003etv_usec \u003d (long) (system_time.wMilliseconds * 1000);\n return 0;\n}\n#endif\n\nunsigned long long us(void)\n{\n\tstruct timeval t;\n\n\tgettimeofday(\u0026t, NULL);\n\n\treturn (t.tv_sec * 1000000ull) + t.tv_usec;\n}\n\nstatic void\nlws_try_client_connection(struct lws_client_connect_info *i, int m)\n{\n\tchar path[128];\n\n\tif (numbered) {\n\t\tlws_snprintf(path, sizeof(path), \u0022/%d.png\u0022, m + 1);\n\t\ti-\u003epath \u003d path;\n\t} else\n\t\ti-\u003epath \u003d urlpath;\n\n\ti-\u003epwsi \u003d \u0026client_wsi[m];\n\ti-\u003eopaque_user_data \u003d (void *)(intptr_t)m;\n\n\tif (!lws_client_connect_via_info(i)) {\n\t\tfailed++;\n\t\tif (++completed \u003d\u003d count) {\n\t\t\tlwsl_user(\u0022Done: failed: %d\u005cn\u0022, failed);\n\t\t\tlws_context_destroy(context);\n\t\t}\n\t} else\n\t\tlwsl_user(\u0022started connection %p: idx %d (%s)\u005cn\u0022,\n\t\t\t client_wsi[m], m, i-\u003epath);\n}\n\nstatic void\nstagger_cb(lws_sorted_usec_list_t *sul)\n{\n\tlws_usec_t next;\n\n\t/*\n\t * open the connections at 100ms intervals, with the\n\t * last one being after 1s, testing both queuing, and\n\t * direct H2 stream addition stability\n\t */\n\tlws_try_client_connection(\u0026i, stagger_idx++);\n\n\tif (stagger_idx \u003d\u003d count)\n\t\treturn;\n\n\tnext \u003d 300 * LWS_US_PER_MS;\n\tif (stagger_idx \u003d\u003d count - 1)\n\t\tnext +\u003d 700 * LWS_US_PER_MS;\n\n\tlws_sul_schedule(context, 0, \u0026sul_stagger, stagger_cb, next);\n}\n\nint main(int argc, const char **argv)\n{\n\tstruct lws_context_creation_info info;\n\tunsigned long long start;\n\tint m, staggered \u003d 0;\n\tconst char *p;\n\n\tmemset(\u0026info, 0, sizeof info); /* otherwise uninitialized garbage */\n\tmemset(\u0026i, 0, sizeof i); /* otherwise uninitialized garbage */\n\n\tlws_cmdline_option_handle_builtin(argc, argv, \u0026info);\n\n\tinfo.signal_cb \u003d signal_cb;\n\tinfo.options \u003d LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;\n\n\tif (lws_cmdline_option(argc, argv, \u0022--uv\u0022))\n\t\tinfo.options |\u003d LWS_SERVER_OPTION_LIBUV;\n\telse\n\t\tif (lws_cmdline_option(argc, argv, \u0022--event\u0022))\n\t\t\tinfo.options |\u003d LWS_SERVER_OPTION_LIBEVENT;\n\t\telse\n\t\t\tif (lws_cmdline_option(argc, argv, \u0022--ev\u0022))\n\t\t\t\tinfo.options |\u003d LWS_SERVER_OPTION_LIBEV;\n\t\t\telse\n\t\t\t\tif (lws_cmdline_option(argc, argv, \u0022--glib\u0022))\n\t\t\t\t\tinfo.options |\u003d LWS_SERVER_OPTION_GLIB;\n\t\t\t\telse\n\t\t\t\t\tsignal(SIGINT, sigint_handler);\n\n\tstaggered \u003d !!lws_cmdline_option(argc, argv, \u0022-s\u0022);\n\n\tlwsl_user(\u0022LWS minimal http client [-s (staggered)] [-p (pipeline)]\u005cn\u0022);\n\tlwsl_user(\u0022 [--h1 (http/1 only)] [-l (localhost)] [-d \u003clogs\u003e]\u005cn\u0022);\n\tlwsl_user(\u0022 [-n (numbered)] [--post]\u005cn\u0022);\n\n\tinfo.port \u003d CONTEXT_PORT_NO_LISTEN; /* we do not run any server */\n\tinfo.protocols \u003d protocols;\n\t/*\n\t * since we know this lws context is only ever going to be used with\n\t * COUNT client wsis / fds / sockets at a time, let lws know it doesn't\n\t * have to use the default allocations for fd tables up to ulimit -n.\n\t * It will just allocate for 1 internal and COUNT + 1 (allowing for h2\n\t * network wsi) that we will use.\n\t */\n\tinfo.fd_limit_per_thread \u003d 1 + COUNT + 1;\n\n#if defined(LWS_WITH_MBEDTLS)\n\t/*\n\t * OpenSSL uses the system trust store. mbedTLS has to be told which\n\t * CA to trust explicitly.\n\t */\n\tinfo.client_ssl_ca_filepath \u003d \u0022./warmcat.com.cer\u0022;\n#endif\n\n\tif ((p \u003d lws_cmdline_option(argc, argv, \u0022--limit\u0022)))\n\t\tinfo.simultaneous_ssl_restriction \u003d atoi(p);\n\n#if defined(LWS_WITH_DETAILED_LATENCY)\n\tinfo.detailed_latency_cb \u003d lws_det_lat_plot_cb;\n\tinfo.detailed_latency_filepath \u003d \u0022/tmp/lws-latency-results\u0022;\n#endif\n\n\tcontext \u003d lws_create_context(\u0026info);\n\tif (!context) {\n\t\tlwsl_err(\u0022lws init failed\u005cn\u0022);\n\t\treturn 1;\n\t}\n\n\ti.context \u003d context;\n\ti.ssl_connection \u003d LCCSCF_USE_SSL |\n\t\t\t LCCSCF_H2_QUIRK_OVERFLOWS_TXCR |\n\t\t\t LCCSCF_H2_QUIRK_NGHTTP2_END_STREAM;\n\n\tif (lws_cmdline_option(argc, argv, \u0022--post\u0022)) {\n\t\tposting \u003d 1;\n\t\ti.method \u003d \u0022POST\u0022;\n\t\ti.ssl_connection |\u003d LCCSCF_HTTP_MULTIPART_MIME;\n\t} else\n\t\ti.method \u003d \u0022GET\u0022;\n\n\t/* enables h1 or h2 connection sharing */\n\tif (lws_cmdline_option(argc, argv, \u0022-p\u0022))\n\t\ti.ssl_connection |\u003d LCCSCF_PIPELINE;\n\n\t/* force h1 even if h2 available */\n\tif (lws_cmdline_option(argc, argv, \u0022--h1\u0022))\n\t\ti.alpn \u003d \u0022http/1.1\u0022;\n\n\tstrcpy(urlpath, \u0022/\u0022);\n\n\tif (lws_cmdline_option(argc, argv, \u0022-l\u0022)) {\n\t\ti.port \u003d 7681;\n\t\ti.address \u003d \u0022localhost\u0022;\n\t\ti.ssl_connection |\u003d LCCSCF_ALLOW_SELFSIGNED;\n\t\tif (posting)\n\t\t\tstrcpy(urlpath, \u0022/formtest\u0022);\n\t} else {\n\t\ti.port \u003d 443;\n\t\ti.address \u003d \u0022libwebsockets.org\u0022;\n\t\tif (posting)\n\t\t\tstrcpy(urlpath, \u0022/testserver/formtest\u0022);\n\t}\n\n\tif (lws_cmdline_option(argc, argv, \u0022--no-tls\u0022))\n\t\ti.ssl_connection \u0026\u003d ~(LCCSCF_USE_SSL);\n\n\tif (lws_cmdline_option(argc, argv, \u0022-n\u0022))\n\t\tnumbered \u003d 1;\n\n\tif ((p \u003d lws_cmdline_option(argc, argv, \u0022--server\u0022)))\n\t\ti.address \u003d p;\n\n\tif ((p \u003d lws_cmdline_option(argc, argv, \u0022--port\u0022)))\n\t\ti.port \u003d atoi(p);\n\n\tif ((p \u003d lws_cmdline_option(argc, argv, \u0022--path\u0022)))\n\t\tlws_strncpy(urlpath, p, sizeof(urlpath));\n\n\tif ((p \u003d lws_cmdline_option(argc, argv, \u0022-c\u0022)))\n\t\tif (atoi(p) \u003c\u003d COUNT \u0026\u0026 atoi(p))\n\t\t\tcount \u003d atoi(p);\n\n\ti.host \u003d i.address;\n\ti.origin \u003d i.address;\n\ti.protocol \u003d protocols[0].name;\n\n\tif (!staggered)\n\t\t/*\n\t\t * just pile on all the connections at once, testing the\n\t\t * pipeline queuing before the first is connected\n\t\t */\n\t\tfor (m \u003d 0; m \u003c count; m++)\n\t\t\tlws_try_client_connection(\u0026i, m);\n\telse\n\t\t/*\n\t\t * delay the connections slightly\n\t\t */\n\t\tlws_sul_schedule(context, 0, \u0026sul_stagger, stagger_cb,\n\t\t\t\t 100 * LWS_US_PER_MS);\n\n\tstart \u003d us();\n\twhile (!intr \u0026\u0026 !lws_service(context, 0))\n\t\t;\n\n\tlwsl_user(\u0022Duration: %lldms\u005cn\u0022, (us() - start) / 1000);\n\tlws_context_destroy(context);\n\n\tlwsl_user(\u0022Exiting with %d\u005cn\u0022, failed || completed !\u003d count);\n\n\treturn failed || completed !\u003d count;\n}\n","s":{"c":1594301272,"u": 426}} ],"g": 4765,"chitpc": 0,"ehitpc": 0,"indexed":0 , "ab": 1, "si": 0, "db":0, "di":0, "sat":0, "lfc": "0000"}