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 19static int interrupted, bad = 1, status; 20static struct lws *client_wsi; 21 22static int 23callback_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 153static 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 162static void 163sigint_handler(int sig) 164{ 165 interrupted = 1; 166} 167 168int 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