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