1 /*
2 * lws-minimal-http-client
3 *
4 * Written in 2010-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 demonstrates the a minimal http client using lws.
10 *
11 * It visits https://warmcat.com/ and receives the html page there. You
12 * 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
19 static int interrupted, bad = 1, status;
20 static struct lws *client_wsi;
21
22 static int
callback_http(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)23 callback_http(struct lws *wsi, enum lws_callback_reasons reason,
24 void *user, void *in, size_t len)
25 {
26 uint8_t buf[1280];
27 union lws_tls_cert_info_results *ci =
28 (union lws_tls_cert_info_results *)buf;
29 #if defined(LWS_HAVE_CTIME_R)
30 char date[32];
31 #endif
32
33 switch (reason) {
34
35 /* because we are protocols[0] ... */
36 case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
37 lwsl_err("CLIENT_CONNECTION_ERROR: %s\n",
38 in ? (char *)in : "(null)");
39 client_wsi = NULL;
40 break;
41
42 case LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP:
43 status = (int)lws_http_client_http_response(wsi);
44 lwsl_notice("lws_http_client_http_response %d\n", status);
45
46 if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_COMMON_NAME,
47 ci, sizeof(buf) - sizeof(*ci)))
48 lwsl_notice(" Peer Cert CN : %s\n", ci->ns.name);
49
50 if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_ISSUER_NAME,
51 ci, sizeof(ci->ns.name)))
52 lwsl_notice(" Peer Cert issuer : %s\n", ci->ns.name);
53
54 if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_VALIDITY_FROM,
55 ci, 0))
56 #if defined(LWS_HAVE_CTIME_R)
57 lwsl_notice(" Peer Cert Valid from: %s",
58 ctime_r(&ci->time, date));
59 #else
60 lwsl_notice(" Peer Cert Valid from: %s",
61 ctime(&ci->time));
62 #endif
63 if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_VALIDITY_TO,
64 ci, 0))
65 #if defined(LWS_HAVE_CTIME_R)
66 lwsl_notice(" Peer Cert Valid to : %s",
67 ctime_r(&ci->time, date));
68 #else
69 lwsl_notice(" Peer Cert Valid to : %s",
70 ctime(&ci->time));
71 #endif
72 if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_USAGE,
73 ci, 0))
74 lwsl_notice(" Peer Cert usage bits: 0x%x\n", ci->usage);
75 if (!lws_tls_peer_cert_info(wsi,
76 LWS_TLS_CERT_INFO_OPAQUE_PUBLIC_KEY,
77 ci, sizeof(buf) - sizeof(*ci))) {
78 lwsl_notice(" Peer Cert public key:\n");
79 lwsl_hexdump_notice(ci->ns.name, (unsigned int)ci->ns.len);
80 }
81
82 if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_AUTHORITY_KEY_ID,
83 ci, 0)) {
84 lwsl_notice(" AUTHORITY_KEY_ID\n");
85 lwsl_hexdump_notice(ci->ns.name, (size_t)ci->ns.len);
86 }
87 if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_AUTHORITY_KEY_ID_ISSUER,
88 ci, 0)) {
89 lwsl_notice(" AUTHORITY_KEY_ID ISSUER\n");
90 lwsl_hexdump_notice(ci->ns.name, (size_t)ci->ns.len);
91 }
92 if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_AUTHORITY_KEY_ID_SERIAL,
93 ci, 0)) {
94 lwsl_notice(" AUTHORITY_KEY_ID SERIAL\n");
95 lwsl_hexdump_notice(ci->ns.name, (size_t)ci->ns.len);
96 }
97 if (!lws_tls_peer_cert_info(wsi, LWS_TLS_CERT_INFO_SUBJECT_KEY_ID,
98 ci, 0)) {
99 lwsl_notice(" AUTHORITY_KEY_ID SUBJECT_KEY_ID\n");
100 lwsl_hexdump_notice(ci->ns.name, (size_t)ci->ns.len);
101 }
102
103 break;
104
105 /* chunks of chunked content, with header removed */
106 case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ:
107 lwsl_user("RECEIVE_CLIENT_HTTP_READ: read %d\n", (int)len);
108 #if 0 /* enable to dump the html */
109 {
110 const char *p = in;
111
112 while (len--)
113 if (*p < 0x7f)
114 putchar(*p++);
115 else
116 putchar('.');
117 }
118 #endif
119 return 0; /* don't passthru */
120
121 /* uninterpreted http content */
122 case LWS_CALLBACK_RECEIVE_CLIENT_HTTP:
123 {
124 char buffer[1024 + LWS_PRE];
125 char *px = buffer + LWS_PRE;
126 int lenx = sizeof(buffer) - LWS_PRE;
127
128 if (lws_http_client_read(wsi, &px, &lenx) < 0)
129 return -1;
130 }
131 return 0; /* don't passthru */
132
133 case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
134 lwsl_user("LWS_CALLBACK_COMPLETED_CLIENT_HTTP\n");
135 client_wsi = NULL;
136 bad = status != 200;
137 lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */
138 break;
139
140 case LWS_CALLBACK_CLOSED_CLIENT_HTTP:
141 client_wsi = NULL;
142 bad = status != 200;
143 lws_cancel_service(lws_get_context(wsi)); /* abort poll wait */
144 break;
145
146 default:
147 break;
148 }
149
150 return lws_callback_http_dummy(wsi, reason, user, in, len);
151 }
152
153 static const struct lws_protocols protocols[] = {
154 {
155 "http",
156 callback_http,
157 0, 0, 0, NULL, 0
158 },
159 LWS_PROTOCOL_LIST_TERM
160 };
161
162 static void
sigint_handler(int sig)163 sigint_handler(int sig)
164 {
165 interrupted = 1;
166 }
167
main(int argc,const char ** argv)168 int main(int argc, const char **argv)
169 {
170 struct lws_context_creation_info info;
171 struct lws_client_connect_info i;
172 struct lws_context *context;
173 const char *p;
174 int n = 0, logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE
175 /*
176 * For LLL_ verbosity above NOTICE to be built into lws,
177 * lws must have been configured and built with
178 * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE
179 *
180 * | LLL_INFO | LLL_PARSER | LLL_HEADER | LLL_EXT |
181 * LLL_CLIENT | LLL_LATENCY | LLL_DEBUG
182 */ ;
183
184 signal(SIGINT, sigint_handler);
185
186 if ((p = lws_cmdline_option(argc, argv, "-d")))
187 logs = atoi(p);
188
189 lws_set_log_level(logs, NULL);
190 lwsl_user("LWS minimal http client [<-d <verbosity>] [-l] [--h1]\n");
191
192 memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
193 info.options = LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
194 info.port = CONTEXT_PORT_NO_LISTEN; /* we do not run any server */
195 info.protocols = protocols;
196 /*
197 * since we know this lws context is only ever going to be used with
198 * one client wsis / fds / sockets at a time, let lws know it doesn't
199 * have to use the default allocations for fd tables up to ulimit -n.
200 * It will just allocate for 1 internal and 1 (+ 1 http2 nwsi) that we
201 * will use.
202 */
203 info.fd_limit_per_thread = 1 + 1 + 1;
204
205 #if defined(LWS_WITH_MBEDTLS) || defined(USE_WOLFSSL)
206 /*
207 * OpenSSL uses the system trust store. mbedTLS has to be told which
208 * CA to trust explicitly.
209 */
210 info.client_ssl_ca_filepath = "./warmcat.com.cer";
211 #endif
212
213 context = lws_create_context(&info);
214 if (!context) {
215 lwsl_err("lws init failed\n");
216 return 1;
217 }
218
219 memset(&i, 0, sizeof i); /* otherwise uninitialized garbage */
220 i.context = context;
221 i.ssl_connection = LCCSCF_USE_SSL;
222
223 if (lws_cmdline_option(argc, argv, "-l")) {
224 i.port = 7681;
225 i.address = "localhost";
226 i.ssl_connection |= LCCSCF_ALLOW_SELFSIGNED;
227 } else {
228 i.port = 443;
229 i.address = "warmcat.com";
230 }
231
232 if ((p = lws_cmdline_option(argc, argv, "-s")))
233 i.address = p;
234
235 i.path = "/";
236 i.host = i.address;
237 i.origin = i.address;
238
239 /* force h1 even if h2 available */
240 if (lws_cmdline_option(argc, argv, "--h1"))
241 i.alpn = "http/1.1";
242
243 i.method = "GET";
244
245 i.protocol = protocols[0].name;
246 i.pwsi = &client_wsi;
247 lws_client_connect_via_info(&i);
248
249 while (n >= 0 && client_wsi && !interrupted)
250 n = lws_service(context, 0);
251
252 lws_context_destroy(context);
253 lwsl_user("Completed: %s\n", bad ? "failed" : "OK");
254
255 return bad;
256 }
257