1/*
2 * lws-minimal-http-client hugeurl
3 *
4 * Written in 2010-2019 by Andy Green <andy@warmcat.com>
5 *
6 * This file is made available under the Creative Commons CC0 1.0
7 * Universal Public Domain Dedication.
8 *
9 * This demonstrates the a minimal http client using lws.
10 *
11 * It visits https://warmcat.com/?fakeparam=<2KB> and receives the html
12 * page there.  You can dump the page data by changing the #if 0 below.
13 */
14
15#include <libwebsockets.h>
16#include <string.h>
17#include <signal.h>
18
19static int interrupted, bad = 1, status;
20static struct lws *client_wsi;
21
22static const char * const uri =
23	"/?fakeparam="
24	"00000000000000000000000000000000000000000000000000"
25	"00000000000000000000000000000000000000000000000000"
26	"00000000000000000000000000000000000000000000000000"
27	"00000000000000000000000000000000000000000000000000"
28	"00000000000000000000000000000000000000000000000000"
29	"00000000000000000000000000000000000000000000000000"
30	"00000000000000000000000000000000000000000000000000"
31	"00000000000000000000000000000000000000000000000000"
32	"00000000000000000000000000000000000000000000000000"
33	"00000000000000000000000000000000000000000000000000" /* 500 */
34	"11111111111111111111111111111111111111111111111111"
35	"11111111111111111111111111111111111111111111111111"
36	"11111111111111111111111111111111111111111111111111"
37	"11111111111111111111111111111111111111111111111111"
38	"11111111111111111111111111111111111111111111111111"
39	"11111111111111111111111111111111111111111111111111"
40	"11111111111111111111111111111111111111111111111111"
41	"11111111111111111111111111111111111111111111111111"
42	"11111111111111111111111111111111111111111111111111"
43	"11111111111111111111111111111111111111111111111111" /* 1000 */
44	"22222222222222222222222222222222222222222222222222"
45	"22222222222222222222222222222222222222222222222222"
46	"22222222222222222222222222222222222222222222222222"
47	"22222222222222222222222222222222222222222222222222"
48	"22222222222222222222222222222222222222222222222222"
49	"22222222222222222222222222222222222222222222222222"
50	"22222222222222222222222222222222222222222222222222"
51	"22222222222222222222222222222222222222222222222222"
52	"22222222222222222222222222222222222222222222222222"
53	"22222222222222222222222222222222222222222222222222" /* 1500 */
54	"33333333333333333333333333333333333333333333333333"
55	"33333333333333333333333333333333333333333333333333"
56	"33333333333333333333333333333333333333333333333333"
57	"33333333333333333333333333333333333333333333333333"
58	"33333333333333333333333333333333333333333333333333"
59	"33333333333333333333333333333333333333333333333333"
60	"33333333333333333333333333333333333333333333333333"
61	"33333333333333333333333333333333333333333333333333"
62	"33333333333333333333333333333333333333333333333333"
63	"33333333333333333333333333333333333333333333333333" /* 2000 */
64;
65
66static int
67callback_http(struct lws *wsi, enum lws_callback_reasons reason,
68	      void *user, void *in, size_t len)
69{
70	switch (reason) {
71
72	/* because we are protocols[0] ... */
73	case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
74		lwsl_err("CLIENT_CONNECTION_ERROR: %s\n",
75			 in ? (char *)in : "(null)");
76		client_wsi = NULL;
77		break;
78
79	case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
80		status = (int)lws_http_client_http_response(wsi);
81		lwsl_user("Connected with server response: %d\n", status);
82		break;
83
84	/* chunks of chunked content, with header removed */
85	case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ:
86		lwsl_user("RECEIVE_CLIENT_HTTP_READ: read %d\n", (int)len);
87#if 0  /* enable to dump the html */
88		{
89			const char *p = in;
90
91			while (len--)
92				if (*p < 0x7f)
93					putchar(*p++);
94				else
95					putchar('.');
96		}
97#endif
98		return 0; /* don't passthru */
99
100	/* uninterpreted http content */
101	case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
102		{
103			char buffer[1024 + LWS_PRE];
104			char *px = buffer + LWS_PRE;
105			int lenx = sizeof(buffer) - LWS_PRE;
106
107			if (lws_http_client_read(wsi, &px, &lenx) < 0)
108				return -1;
109		}
110		return 0; /* don't passthru */
111
112	case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
113		client_wsi = NULL;
114		bad = status != 200;
115		lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */
116		break;
117
118	case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
119		client_wsi = NULL;
120		bad = status != 200;
121		lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */
122		break;
123
124	default:
125		break;
126	}
127
128	return lws_callback_http_dummy(wsi, reason, user, in, len);
129}
130
131static const struct lws_protocols protocols[] = {
132	{
133		"http",
134		callback_http,
135		0, 0, 0, NULL, 0
136	},
137	LWS_PROTOCOL_LIST_TERM
138};
139
140static void
141sigint_handler(int sig)
142{
143	interrupted = 1;
144}
145
146int main(int argc, const char **argv)
147{
148	struct lws_context_creation_info info;
149	struct lws_client_connect_info i;
150	struct lws_context *context;
151	int n = 0;
152
153	signal(SIGINT, sigint_handler);
154
155	memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
156	lws_cmdline_option_handle_builtin(argc, argv, &info);
157
158	lwsl_user("LWS minimal http client hugeurl [-d <verbosity>] [-l] [--h1]\n");
159
160	info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
161	info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */
162	info.protocols = protocols;
163	info.pt_serv_buf_size = 8192;
164	info.timeout_secs = 10;
165	info.connect_timeout_secs = 30;
166	/*
167	 * since we know this lws context is only ever going to be used with
168	 * one client wsis / fds / sockets at a time, let lws know it doesn't
169	 * have to use the default allocations for fd tables up to ulimit -n.
170	 * It will just allocate for 1 internal and 1 (+ 1 http2 nwsi) that we
171	 * will use.
172	 */
173	info.fd_limit_per_thread = 1 + 1 + 1;
174
175#if defined(LWS_WITH_MBEDTLS) || defined(USE_WOLFSSL)
176	/*
177	 * OpenSSL uses the system trust store.  mbedTLS has to be told which
178	 * CA to trust explicitly.
179	 */
180	info.client_ssl_ca_filepath = "./warmcat.com.cer";
181#endif
182
183	context = lws_create_context(&info);
184	if (!context) {
185		lwsl_err("lws init failed\n");
186		return 1;
187	}
188
189	memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */
190	i.context = context;
191	i.ssl_connection = LCCSCF_USE_SSL;
192
193	if (lws_cmdline_option(argc, argv, "-l")) {
194		i.port = 7681;
195		i.address = "localhost";
196		i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED;
197	} else {
198		i.port = 443;
199		i.address = "warmcat.com";
200	}
201
202	if (lws_cmdline_option(argc, argv, "--h1"))
203		i.alpn = "http/1.1";
204
205	i.path = uri;
206	i.host = i.address;
207	i.origin = i.address;
208	i.method = "GET";
209	i.protocol = protocols[0].name;
210	i.pwsi = &client_wsi;
211
212	lws_client_connect_via_info(&i);
213
214	while (n >= 0 && client_wsi && !interrupted)
215		n = lws_service(context, 0);
216
217	lws_context_destroy(context);
218	lwsl_user("Completed: %s\n", bad? "failed": "OK");
219
220	return bad;
221}
222