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