1 /*
2 * Copyright (c) 2019 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_REGISTER(net_http_client_sample, LOG_LEVEL_DBG);
9
10 #include <zephyr/net/net_ip.h>
11 #include <zephyr/net/socket.h>
12 #include <zephyr/net/tls_credentials.h>
13 #include <zephyr/net/http/client.h>
14
15 #include "ca_certificate.h"
16
17 #define HTTP_PORT 8000
18 #define HTTPS_PORT 4443
19
20 #if defined(CONFIG_NET_CONFIG_PEER_IPV6_ADDR)
21 #define SERVER_ADDR6 CONFIG_NET_CONFIG_PEER_IPV6_ADDR
22 #else
23 #define SERVER_ADDR6 ""
24 #endif
25
26 #if defined(CONFIG_NET_CONFIG_PEER_IPV4_ADDR)
27 #define SERVER_ADDR4 CONFIG_NET_CONFIG_PEER_IPV4_ADDR
28 #else
29 #define SERVER_ADDR4 ""
30 #endif
31
32 #define MAX_RECV_BUF_LEN 512
33
34 static uint8_t recv_buf_ipv4[MAX_RECV_BUF_LEN];
35 static uint8_t recv_buf_ipv6[MAX_RECV_BUF_LEN];
36
setup_socket(sa_family_t family,const char * server,int port,int * sock,struct sockaddr * addr,socklen_t addr_len)37 static int setup_socket(sa_family_t family, const char *server, int port,
38 int *sock, struct sockaddr *addr, socklen_t addr_len)
39 {
40 const char *family_str = family == AF_INET ? "IPv4" : "IPv6";
41 int ret = 0;
42
43 memset(addr, 0, addr_len);
44
45 if (family == AF_INET) {
46 net_sin(addr)->sin_family = AF_INET;
47 net_sin(addr)->sin_port = htons(port);
48 inet_pton(family, server, &net_sin(addr)->sin_addr);
49 } else {
50 net_sin6(addr)->sin6_family = AF_INET6;
51 net_sin6(addr)->sin6_port = htons(port);
52 inet_pton(family, server, &net_sin6(addr)->sin6_addr);
53 }
54
55 if (IS_ENABLED(CONFIG_NET_SOCKETS_SOCKOPT_TLS)) {
56 sec_tag_t sec_tag_list[] = {
57 CA_CERTIFICATE_TAG,
58 };
59
60 *sock = socket(family, SOCK_STREAM, IPPROTO_TLS_1_2);
61 if (*sock >= 0) {
62 ret = setsockopt(*sock, SOL_TLS, TLS_SEC_TAG_LIST,
63 sec_tag_list, sizeof(sec_tag_list));
64 if (ret < 0) {
65 LOG_ERR("Failed to set %s secure option (%d)",
66 family_str, -errno);
67 ret = -errno;
68 }
69
70 ret = setsockopt(*sock, SOL_TLS, TLS_HOSTNAME,
71 TLS_PEER_HOSTNAME,
72 sizeof(TLS_PEER_HOSTNAME));
73 if (ret < 0) {
74 LOG_ERR("Failed to set %s TLS_HOSTNAME "
75 "option (%d)", family_str, -errno);
76 ret = -errno;
77 }
78 }
79 } else {
80 *sock = socket(family, SOCK_STREAM, IPPROTO_TCP);
81 }
82
83 if (*sock < 0) {
84 LOG_ERR("Failed to create %s HTTP socket (%d)", family_str,
85 -errno);
86 }
87
88 return ret;
89 }
90
payload_cb(int sock,struct http_request * req,void * user_data)91 static int payload_cb(int sock, struct http_request *req, void *user_data)
92 {
93 const char *content[] = {
94 "foobar",
95 "chunked",
96 "last"
97 };
98 char tmp[64];
99 int i, pos = 0;
100
101 for (i = 0; i < ARRAY_SIZE(content); i++) {
102 pos += snprintk(tmp + pos, sizeof(tmp) - pos,
103 "%x\r\n%s\r\n",
104 (unsigned int)strlen(content[i]),
105 content[i]);
106 }
107
108 pos += snprintk(tmp + pos, sizeof(tmp) - pos, "0\r\n\r\n");
109
110 (void)send(sock, tmp, pos, 0);
111
112 return pos;
113 }
114
response_cb(struct http_response * rsp,enum http_final_call final_data,void * user_data)115 static void response_cb(struct http_response *rsp,
116 enum http_final_call final_data,
117 void *user_data)
118 {
119 if (final_data == HTTP_DATA_MORE) {
120 LOG_INF("Partial data received (%zd bytes)", rsp->data_len);
121 } else if (final_data == HTTP_DATA_FINAL) {
122 LOG_INF("All the data received (%zd bytes)", rsp->data_len);
123 }
124
125 LOG_INF("Response to %s", (const char *)user_data);
126 LOG_INF("Response status %s", rsp->http_status);
127 }
128
connect_socket(sa_family_t family,const char * server,int port,int * sock,struct sockaddr * addr,socklen_t addr_len)129 static int connect_socket(sa_family_t family, const char *server, int port,
130 int *sock, struct sockaddr *addr, socklen_t addr_len)
131 {
132 int ret;
133
134 ret = setup_socket(family, server, port, sock, addr, addr_len);
135 if (ret < 0 || *sock < 0) {
136 return -1;
137 }
138
139 ret = connect(*sock, addr, addr_len);
140 if (ret < 0) {
141 LOG_ERR("Cannot connect to %s remote (%d)",
142 family == AF_INET ? "IPv4" : "IPv6",
143 -errno);
144 close(*sock);
145 *sock = -1;
146 ret = -errno;
147 }
148
149 return ret;
150 }
151
run_queries(void)152 static int run_queries(void)
153 {
154 struct sockaddr_in6 addr6;
155 struct sockaddr_in addr4;
156 int sock4 = -1, sock6 = -1;
157 int32_t timeout = 3 * MSEC_PER_SEC;
158 int ret = 0;
159 int port = HTTP_PORT;
160
161 if (IS_ENABLED(CONFIG_NET_SOCKETS_SOCKOPT_TLS)) {
162 ret = tls_credential_add(CA_CERTIFICATE_TAG,
163 TLS_CREDENTIAL_CA_CERTIFICATE,
164 ca_certificate,
165 sizeof(ca_certificate));
166 if (ret < 0) {
167 LOG_ERR("Failed to register public certificate: %d",
168 ret);
169 return ret;
170 }
171
172 port = HTTPS_PORT;
173 }
174
175 if (IS_ENABLED(CONFIG_NET_IPV4)) {
176 (void)connect_socket(AF_INET, SERVER_ADDR4, port,
177 &sock4, (struct sockaddr *)&addr4,
178 sizeof(addr4));
179 }
180
181 if (IS_ENABLED(CONFIG_NET_IPV6)) {
182 (void)connect_socket(AF_INET6, SERVER_ADDR6, port,
183 &sock6, (struct sockaddr *)&addr6,
184 sizeof(addr6));
185 }
186
187 if (sock4 < 0 && sock6 < 0) {
188 LOG_ERR("Cannot create HTTP connection.");
189 return -ECONNABORTED;
190 }
191
192 if (sock4 >= 0 && IS_ENABLED(CONFIG_NET_IPV4)) {
193 struct http_request req;
194
195 memset(&req, 0, sizeof(req));
196
197 req.method = HTTP_GET;
198 req.url = "/";
199 req.host = SERVER_ADDR4;
200 req.protocol = "HTTP/1.1";
201 req.response = response_cb;
202 req.recv_buf = recv_buf_ipv4;
203 req.recv_buf_len = sizeof(recv_buf_ipv4);
204
205 ret = http_client_req(sock4, &req, timeout, "IPv4 GET");
206
207 close(sock4);
208 }
209
210 if (sock6 >= 0 && IS_ENABLED(CONFIG_NET_IPV6)) {
211 struct http_request req;
212
213 memset(&req, 0, sizeof(req));
214
215 req.method = HTTP_GET;
216 req.url = "/";
217 req.host = SERVER_ADDR6;
218 req.protocol = "HTTP/1.1";
219 req.response = response_cb;
220 req.recv_buf = recv_buf_ipv6;
221 req.recv_buf_len = sizeof(recv_buf_ipv6);
222
223 ret = http_client_req(sock6, &req, timeout, "IPv6 GET");
224
225 close(sock6);
226 }
227
228 sock4 = -1;
229 sock6 = -1;
230
231 if (IS_ENABLED(CONFIG_NET_IPV4)) {
232 (void)connect_socket(AF_INET, SERVER_ADDR4, port,
233 &sock4, (struct sockaddr *)&addr4,
234 sizeof(addr4));
235 }
236
237 if (IS_ENABLED(CONFIG_NET_IPV6)) {
238 (void)connect_socket(AF_INET6, SERVER_ADDR6, port,
239 &sock6, (struct sockaddr *)&addr6,
240 sizeof(addr6));
241 }
242
243 if (sock4 < 0 && sock6 < 0) {
244 LOG_ERR("Cannot create HTTP connection.");
245 return -ECONNABORTED;
246 }
247
248 if (sock4 >= 0 && IS_ENABLED(CONFIG_NET_IPV4)) {
249 struct http_request req;
250
251 memset(&req, 0, sizeof(req));
252
253 req.method = HTTP_POST;
254 req.url = "/foobar";
255 req.host = SERVER_ADDR4;
256 req.protocol = "HTTP/1.1";
257 req.payload = "foobar";
258 req.payload_len = strlen(req.payload);
259 req.response = response_cb;
260 req.recv_buf = recv_buf_ipv4;
261 req.recv_buf_len = sizeof(recv_buf_ipv4);
262
263 ret = http_client_req(sock4, &req, timeout, "IPv4 POST");
264
265 close(sock4);
266 }
267
268 if (sock6 >= 0 && IS_ENABLED(CONFIG_NET_IPV6)) {
269 struct http_request req;
270
271 memset(&req, 0, sizeof(req));
272
273 req.method = HTTP_POST;
274 req.url = "/";
275 req.host = SERVER_ADDR6;
276 req.protocol = "HTTP/1.1";
277 req.payload = "foobar";
278 req.payload_len = strlen(req.payload);
279 req.response = response_cb;
280 req.recv_buf = recv_buf_ipv6;
281 req.recv_buf_len = sizeof(recv_buf_ipv6);
282
283 ret = http_client_req(sock6, &req, timeout, "IPv6 POST");
284
285 close(sock6);
286 }
287
288 /* Do a chunked POST request */
289
290 sock4 = -1;
291 sock6 = -1;
292
293 if (IS_ENABLED(CONFIG_NET_IPV4)) {
294 (void)connect_socket(AF_INET, SERVER_ADDR4, port,
295 &sock4, (struct sockaddr *)&addr4,
296 sizeof(addr4));
297 }
298
299 if (IS_ENABLED(CONFIG_NET_IPV6)) {
300 (void)connect_socket(AF_INET6, SERVER_ADDR6, port,
301 &sock6, (struct sockaddr *)&addr6,
302 sizeof(addr6));
303 }
304
305 if (sock4 < 0 && sock6 < 0) {
306 LOG_ERR("Cannot create HTTP connection.");
307 return -ECONNABORTED;
308 }
309
310 if (sock4 >= 0 && IS_ENABLED(CONFIG_NET_IPV4)) {
311 struct http_request req;
312 const char *headers[] = {
313 "Transfer-Encoding: chunked\r\n",
314 NULL
315 };
316
317 memset(&req, 0, sizeof(req));
318
319 req.method = HTTP_POST;
320 req.url = "/chunked-test";
321 req.host = SERVER_ADDR4;
322 req.protocol = "HTTP/1.1";
323 req.payload_cb = payload_cb;
324 req.header_fields = headers;
325 req.response = response_cb;
326 req.recv_buf = recv_buf_ipv4;
327 req.recv_buf_len = sizeof(recv_buf_ipv4);
328
329 ret = http_client_req(sock4, &req, timeout, "IPv4 POST");
330
331 close(sock4);
332 }
333
334 if (sock6 >= 0 && IS_ENABLED(CONFIG_NET_IPV6)) {
335 struct http_request req;
336 const char *headers[] = {
337 "Transfer-Encoding: chunked\r\n",
338 NULL
339 };
340
341 memset(&req, 0, sizeof(req));
342
343 req.method = HTTP_POST;
344 req.url = "/chunked-test";
345 req.host = SERVER_ADDR6;
346 req.protocol = "HTTP/1.1";
347 req.payload_cb = payload_cb;
348 req.header_fields = headers;
349 req.response = response_cb;
350 req.recv_buf = recv_buf_ipv6;
351 req.recv_buf_len = sizeof(recv_buf_ipv6);
352
353 ret = http_client_req(sock6, &req, timeout, "IPv6 POST");
354
355 close(sock6);
356 }
357
358 return ret;
359 }
360
main(void)361 int main(void)
362 {
363 int iterations = CONFIG_NET_SAMPLE_SEND_ITERATIONS;
364 int i = 0;
365 int ret = 0;
366
367 while (iterations == 0 || i < iterations) {
368 ret = run_queries();
369 if (ret < 0) {
370 ret = 1;
371 break;
372 }
373
374 if (iterations > 0) {
375 i++;
376 if (i >= iterations) {
377 ret = 0;
378 break;
379 }
380 } else {
381 ret = 0;
382 break;
383 }
384 }
385
386 if (iterations == 0) {
387 k_sleep(K_FOREVER);
388 }
389
390 exit(ret);
391 return ret;
392 }
393