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